Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
}

/// Given the bounds on an object, determines what single region bound (if any) we can
/// use to summarize this type.
///
/// The basic idea is that we will use the bound the user
/// provided, if they provided one, and otherwise search the supertypes of trait bounds
/// for region bounds. It may be that we can derive no bound at all, in which case
/// we return `None`.
#[instrument(level = "debug", skip(self, span), ret)]
fn compute_object_lifetime_bound(
&self,
span: Span,
existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
) -> Option<ty::Region<'tcx>> // if None, use the default
{
let tcx = self.tcx();

// No explicit region bound specified. Therefore, examine trait
// bounds and see if we can derive region bounds from those.
let derived_region_bounds = traits::wf::object_region_bounds(tcx, existential_predicates);

// If there are no derived region bounds, then report back that we
// can find no region bound. The caller will use the default.
if derived_region_bounds.is_empty() {
return None;
}

// If any of the derived region bounds are 'static, that is always
// the best choice.
if derived_region_bounds.iter().any(|r| r.is_static()) {
return Some(tcx.lifetimes.re_static);
}

// Determine whether there is exactly one unique region in the set
// of derived region bounds. If so, use that. Otherwise, report an
// error.
let r = derived_region_bounds[0];
if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
self.dcx().emit_err(crate::errors::AmbiguousLifetimeBound { span });
}
Some(r)
}

/// Prohibit or lint against *bare* trait object types depending on the edition.
///
/// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`.
Expand Down
191 changes: 189 additions & 2 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,141 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
}

pub(super) fn report_ambiguous_assoc_item(
&self,
bound1: ty::PolyTraitRef<'tcx>,
bound2: ty::PolyTraitRef<'tcx>,
matching_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
qself: AssocItemQSelf,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
span: Span,
constraint: Option<&hir::AssocItemConstraint<'tcx>>,
) -> ErrorGuaranteed {
let tcx = self.tcx();

let assoc_kind_str = assoc_tag_str(assoc_tag);
let qself_str = qself.to_string(tcx);
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
span,
assoc_kind: assoc_kind_str,
assoc_ident,
qself: &qself_str,
});
// Provide a more specific error code index entry for equality bindings.
err.code(
if let Some(constraint) = constraint
&& let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind
{
E0222
} else {
E0221
},
);

// FIXME(#97583): Print associated item bindings properly (i.e., not as equality
// predicates!).
// FIXME: Turn this into a structured, translatable & more actionable suggestion.
let mut where_bounds = vec![];
for bound in [bound1, bound2].into_iter().chain(matching_candidates) {
let bound_id = bound.def_id();
let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind(
tcx,
assoc_ident,
assoc_tag,
bound_id,
);
let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id));

if let Some(bound_span) = bound_span {
err.span_label(
bound_span,
format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),),
);
if let Some(constraint) = constraint {
match constraint.kind {
hir::AssocItemConstraintKind::Equality { term } => {
let term: ty::Term<'_> = match term {
hir::Term::Ty(ty) => self.lower_ty(ty).into(),
hir::Term::Const(ct) => {
let assoc_item =
assoc_item.expect("assoc_item should be present");
let projection_term = bound.map_bound(|trait_ref| {
let item_segment = hir::PathSegment {
ident: constraint.ident,
hir_id: constraint.hir_id,
res: Res::Err,
args: Some(constraint.gen_args),
infer_args: false,
};

let alias_args = self.lower_generic_args_of_assoc_item(
constraint.ident.span,
assoc_item.def_id,
&item_segment,
trait_ref.args,
);
ty::AliasTerm::new_from_def_id(
tcx,
assoc_item.def_id,
alias_args,
)
});

// FIXME(mgca): code duplication with other places we lower
// the rhs' of associated const bindings
let ty = projection_term.map_bound(|alias| {
tcx.type_of(alias.def_id())
.instantiate(tcx, alias.args)
.skip_norm_wip()
});
let ty = super::bounds::check_assoc_const_binding_type(
self,
constraint.ident,
ty,
constraint.hir_id,
);

self.lower_const_arg(ct, ty).into()
}
};
if term.references_error() {
continue;
}
// FIXME(#97583): This isn't syntactically well-formed!
where_bounds.push(format!(
" T: {trait}::{assoc_ident} = {term}",
trait = bound.print_only_trait_path(),
));
}
// FIXME: Provide a suggestion.
hir::AssocItemConstraintKind::Bound { bounds: _ } => {}
}
} else {
err.span_suggestion_verbose(
span.with_hi(assoc_ident.span.lo()),
"use fully-qualified syntax to disambiguate",
format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
Applicability::MaybeIncorrect,
);
}
} else {
let trait_ = tcx.short_string(bound.print_only_trait_path(), err.long_ty_path());
err.note(format!(
"associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`",
));
}
}
if !where_bounds.is_empty() {
err.help(format!(
"consider introducing a new type parameter `T` and adding `where` constraints:\
\n where\n T: {qself_str},\n{}",
where_bounds.join(",\n"),
));
}
err.emit()
}

pub(crate) fn report_missing_self_ty_for_resolved_path(
&self,
trait_def_id: DefId,
Expand Down Expand Up @@ -568,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

pub(super) fn report_ambiguous_assoc_item_path(
fn report_ambiguous_assoc_item_path(
&self,
span: Span,
types: &[String],
Expand Down Expand Up @@ -1847,7 +1982,59 @@ fn generics_args_err_extend<'a>(
}
}

pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str {
pub(super) struct AmbiguityBetweenVariantAndAssocItem<'tcx> {
pub(super) variant_def_id: DefId,
pub(super) item_def_id: DefId,
pub(super) span: Span,
pub(super) segment_ident: Ident,
pub(super) bound_def_id: DefId,
pub(super) self_ty: Ty<'tcx>,
pub(super) tcx: TyCtxt<'tcx>,
pub(super) mode: super::LowerTypeRelativePathMode,
}

impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAssocItem<'tcx> {
fn into_diag(
self,
dcx: rustc_errors::DiagCtxtHandle<'a>,
level: rustc_errors::Level,
) -> Diag<'a, ()> {
let Self {
variant_def_id,
item_def_id,
span,
segment_ident,
bound_def_id,
self_ty,
tcx,
mode,
} = self;
let mut lint = Diag::new(dcx, level, "ambiguous associated item");

let mut could_refer_to = |kind: DefKind, def_id, also| {
let note_msg = format!(
"`{}` could{} refer to the {} defined here",
segment_ident,
also,
tcx.def_kind_descr(kind, def_id)
);
lint.span_note(tcx.def_span(def_id), note_msg);
};

could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also");

lint.span_suggestion(
span,
"use fully-qualified syntax",
format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident),
Applicability::MachineApplicable,
);
lint
}
}

fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str {
match assoc_tag {
ty::AssocTag::Fn => "function",
ty::AssocTag::Const => "constant",
Expand Down
Loading
Loading