Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix type resolution of associated const equality bounds #118360

Closed
wants to merge 3 commits into from
Closed
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
35 changes: 26 additions & 9 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}`
.label = ambiguous associated {$assoc_kind} `{$assoc_name}`

hir_analysis_ambiguous_lifetime_bound =
ambiguous lifetime bound, explicit lifetime bound required

hir_analysis_assoc_bound_on_const = expected associated type, found {$descr}
.note = trait bounds not allowed on {$descr}
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`

hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
[true] an
*[false] a similarly named
} associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind}
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name

hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
.label = unexpected {$got}
.expected_because_label = expected a {$expected} because of this associated {$expected}
.note = the associated {$assoc_kind} is defined here
.bound_on_assoc_const_label = bounds are not allowed on associated constants

hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here

hir_analysis_assoc_type_binding_not_allowed =
associated type bindings are not allowed here
Expand Down Expand Up @@ -265,6 +285,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current

hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate

hir_analysis_param_in_ty_of_assoc_const = the type of the associated constant `{$assoc_const}` must not depend on generic parameters
.label = its type must not depend on the {$param_kind} parameter `{$param_name}`
.param_defined_here_label = the {$param_kind} parameter `{$param_name}` is defined here

hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
.help = add `#![feature(unboxed_closures)]` to the crate attributes to use it

Expand All @@ -280,10 +304,6 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no

hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`

hir_analysis_return_type_notation_conflicting_bound =
ambiguous associated function `{$assoc_name}` for `{$ty_name}`
.note = `{$assoc_name}` is declared in two supertraits: `{$first_bound}` and `{$second_bound}`

hir_analysis_return_type_notation_equality_bound =
return type notation is not allowed to use type equality

Expand All @@ -294,9 +314,6 @@ hir_analysis_return_type_notation_illegal_param_type =
return type notation is not allowed for functions that have type parameters
.label = type parameter declared here

hir_analysis_return_type_notation_missing_method =
cannot find associated function `{$assoc_name}` for `{$ty_name}`

hir_analysis_return_type_notation_on_non_rpitit =
return type notation used on function that is not `async` and does not return `impl Trait`
.note = function returns `{$ty}`, which is not compatible with associated type return bounds
Expand Down
129 changes: 31 additions & 98 deletions compiler/rustc_hir_analysis/src/astconv/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_lint_defs::Applicability;
use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
use rustc_middle::ty::{self as ty, Ty};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
Expand Down Expand Up @@ -256,44 +255,35 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {

let tcx = self.tcx();

let return_type_notation =
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;

let candidate = if return_type_notation {
if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Fn,
binding.item_name,
) {
trait_ref
let assoc_kind =
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
ty::AssocKind::Fn
} else if let ConvertedBindingKind::Equality(term) = binding.kind
&& let ty::TermKind::Const(_) = term.node.unpack()
{
ty::AssocKind::Const
} else {
self.one_bound_for_assoc_method(
traits::supertraits(tcx, trait_ref),
trait_ref.print_only_trait_path(),
binding.item_name,
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
ty::AssocKind::Type
};

let candidate = if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Type,
assoc_kind,
binding.item_name,
) {
// Simple case: X is defined in the current trait.
// Simple case: The assoc item is defined in the current trait.
trait_ref
} else {
// Otherwise, we have to walk through the supertraits to find
// those that do.
self.one_bound_for_assoc_type(
// one that does define it.
self.one_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
None,
assoc_kind,
binding.item_name,
path_span,
match binding.kind {
ConvertedBindingKind::Equality(term) => Some(term),
_ => None,
},
Some(&binding),
)?
};

Expand All @@ -302,18 +292,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {

// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
let find_item_of_kind = |kind| {
tcx.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
};
let assoc_item = if return_type_notation {
find_item_of_kind(ty::AssocKind::Fn)
} else {
find_item_of_kind(ty::AssocKind::Type)
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
}
.expect("missing associated type");
let assoc_item = tcx
.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
.expect("missing associated item");

if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
tcx.sess
Expand All @@ -340,7 +323,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
.or_insert(binding.span);
}

let projection_ty = if return_type_notation {
let projection_ty = if let ty::AssocKind::Fn = assoc_kind {
let mut emitted_bad_param_err = false;
// If we have an method return type bound, then we need to substitute
// the method's early bound params with suitable late-bound params.
Expand Down Expand Up @@ -467,7 +450,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node));
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);

Expand All @@ -492,77 +475,27 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
}
}

let assoc_item_def_id = projection_ty.skip_binder().def_id;
let def_kind = tcx.def_kind(assoc_item_def_id);
match binding.kind {
ConvertedBindingKind::Equality(..) if return_type_notation => {
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
));
}
ConvertedBindingKind::Equality(mut term) => {
ConvertedBindingKind::Equality(term) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
match (def_kind, term.unpack()) {
(DefKind::AssocTy, ty::TermKind::Ty(_))
| (DefKind::AssocConst, ty::TermKind::Const(_)) => (),
(_, _) => {
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
let expected = tcx.def_descr(assoc_item_def_id);
let mut err = tcx.sess.struct_span_err(
binding.span,
format!("expected {expected} bound, found {got}"),
);
err.span_note(
tcx.def_span(assoc_item_def_id),
format!("{expected} defined here"),
);

if let DefKind::AssocConst = def_kind
&& let Some(t) = term.ty()
&& (t.is_enum() || t.references_error())
&& tcx.features().associated_const_equality
{
err.span_suggestion(
binding.span,
"if equating a const, try wrapping with braces",
format!("{} = {{ const }}", binding.item_name),
Applicability::HasPlaceholders,
);
}
let reported = err.emit();
term = match def_kind {
DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
DefKind::AssocConst => ty::Const::new_error(
tcx,
reported,
tcx.type_of(assoc_item_def_id)
.instantiate(tcx, projection_ty.skip_binder().args),
)
.into(),
_ => unreachable!(),
};
}
}
bounds.push_projection_bound(
tcx,
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
projection_ty,
term: term.node,
}),
binding.span,
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
match def_kind {
DefKind::AssocTy => {}
_ => {
return Err(tcx.sess.emit_err(errors::AssocBoundOnConst {
span: assoc_ident.span,
descr: tcx.def_descr(assoc_item_def_id),
}));
}
}
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
//
// `<T as Iterator>::Item: Debug`
Expand Down
Loading
Loading