Skip to content

Commit

Permalink
Detect object safety errors when assoc type is missing
Browse files Browse the repository at this point in the history
When an associated type with GATs isn't specified in a `dyn Trait`, emit
an object safety error instead of only complaining about the missing
associated type, as it will lead the user down a path of three different
errors before letting them know that what they were trying to do is
impossible to begin with.

Fix #103155.
  • Loading branch information
estebank committed Oct 30, 2023
1 parent 6d1fc53 commit 17a6ae2
Show file tree
Hide file tree
Showing 36 changed files with 223 additions and 194 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ hir_analysis_unused_associated_type_bounds =
.suggestion = remove this bound
hir_analysis_value_of_associated_struct_already_specified =
the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified
the value of the associated type `{$item_name}` in trait `{$def_path}` is already specified
.label = re-bound here
.previous_bound_label = `{$item_name}` bound here first
Expand Down
47 changes: 38 additions & 9 deletions compiler/rustc_hir_analysis/src/astconv/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::errors::{
AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion,
};
use crate::traits::error_reporting::report_object_safety_error;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
Expand All @@ -13,6 +14,7 @@ use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_trait_selection::traits::object_safety_violations_for_assoc_item;

use std::collections::BTreeSet;

Expand Down Expand Up @@ -520,24 +522,33 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
})
.collect();
let mut names = vec![];
let mut names: FxHashMap<String, Vec<Symbol>> = Default::default();
let mut names_len = 0;

// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
// `issue-22560.rs`.
let mut trait_bound_spans: Vec<Span> = vec![];
let mut object_safety_violations = false;
for (span, items) in &associated_types {
if !items.is_empty() {
trait_bound_spans.push(*span);
}
for assoc_item in items {
let trait_def_id = assoc_item.container_id(tcx);
names.push(format!(
"`{}` (from trait `{}`)",
assoc_item.name,
tcx.def_path_str(trait_def_id),
));
names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name);
names_len += 1;

let violations =
object_safety_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
if !violations.is_empty() {
report_object_safety_error(tcx, *span, trait_def_id, &violations).emit();
object_safety_violations = true;
}
}
}
if object_safety_violations {
return;
}
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
match bound.trait_ref.path.segments {
// FIXME: `trait_ref.path.span` can point to a full path with multiple
Expand Down Expand Up @@ -573,15 +584,33 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
_ => {}
}
}
names.sort();

let names = names
.into_iter()
.map(|(trait_, assocs)| {
format!(
"{} in `{trait_}`",
match &assocs[..] {
[] => String::new(),
[only] => format!("`{only}`"),
[assocs @ .., last] => format!(
"{} and `{last}`",
assocs.iter().map(|a| format!("`{a}`")).collect::<Vec<_>>().join(", ")
),
}
)
})
.collect::<Vec<String>>()
.join(", ");

trait_bound_spans.sort();
let mut err = struct_span_err!(
tcx.sess,
trait_bound_spans,
E0191,
"the value of the associated type{} {} must be specified",
pluralize!(names.len()),
names.join(", "),
pluralize!(names_len),
names,
);
let mut suggestions = vec![];
let mut types_count = 0;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/astconv/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
from context; please supply an explicit bound"
);
let e = if borrowed {
// We will have already emitted an error E0106 complaining about a
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::object_safety_violations_for_assoc_item;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::project::NormalizeExt;
pub use self::project::{normalize_inherent_projection, normalize_projection_type};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {

/// Returns `Some(_)` if this item makes the containing trait not object safe.
#[instrument(level = "debug", skip(tcx), ret)]
fn object_safety_violations_for_assoc_item(
pub fn object_safety_violations_for_assoc_item(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
item: ty::AssocItem,
Expand Down
Loading

0 comments on commit 17a6ae2

Please sign in to comment.