Skip to content

Commit 455e977

Browse files
authored
Unrolled build for rust-lang#122120
Rollup merge of rust-lang#122120 - fmease:sugg-assoc-ty-bound-on-eq-bound, r=compiler-errors Suggest associated type bounds on problematic associated equality bounds Fixes rust-lang#105056. TL;DR: Suggest `Trait<Ty: Bound>` on `Trait<Ty = Bound>` in Rust >=2021. ~~Blocked on rust-lang#122055 (stabilization of `associated_type_bounds`), I'd say.~~ (merged)
2 parents 47ecded + 3879acb commit 455e977

File tree

10 files changed

+287
-99
lines changed

10 files changed

+287
-99
lines changed

compiler/rustc_hir/src/hir.rs

+8-14
Original file line numberDiff line numberDiff line change
@@ -2289,21 +2289,15 @@ pub enum ImplItemKind<'hir> {
22892289
Type(&'hir Ty<'hir>),
22902290
}
22912291

2292-
/// Bind a type to an associated type (i.e., `A = Foo`).
2292+
/// An associated item binding.
22932293
///
2294-
/// Bindings like `A: Debug` are represented as a special type `A =
2295-
/// $::Debug` that is understood by the HIR ty lowering code.
2294+
/// ### Examples
22962295
///
2297-
/// FIXME(alexreg): why have a separate type for the binding case,
2298-
/// wouldn't it be better to make the `ty` field an enum like the
2299-
/// following?
2300-
///
2301-
/// ```ignore (pseudo-rust)
2302-
/// enum TypeBindingKind {
2303-
/// Equals(...),
2304-
/// Binding(...),
2305-
/// }
2306-
/// ```
2296+
/// * `Trait<A = Ty, B = Ty>`
2297+
/// * `Trait<G<Ty> = Ty>`
2298+
/// * `Trait<A: Bound>`
2299+
/// * `Trait<C = { Ct }>` (under feature `associated_const_equality`)
2300+
/// * `Trait<f(): Bound>` (under feature `return_type_notation`)
23072301
#[derive(Debug, Clone, Copy, HashStable_Generic)]
23082302
pub struct TypeBinding<'hir> {
23092303
pub hir_id: HirId,
@@ -2336,7 +2330,7 @@ impl<'hir> From<AnonConst> for Term<'hir> {
23362330
pub enum TypeBindingKind<'hir> {
23372331
/// E.g., `Foo<Bar: Send>`.
23382332
Constraint { bounds: &'hir [GenericBound<'hir>] },
2339-
/// E.g., `Foo<Bar = ()>`, `Foo<Bar = ()>`
2333+
/// E.g., `Foo<Bar = ()>`.
23402334
Equality { term: Term<'hir> },
23412335
}
23422336

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+107-62
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,85 @@ use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamNa
99
use super::HirTyLowerer;
1010

1111
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
12+
/// Prohibit or lint against *bare* trait object types depending on the edition.
13+
///
14+
/// *Bare* trait object types are ones that aren't preceeded by the keyword `dyn`.
15+
/// In edition 2021 and onward we emit a hard error for them.
16+
pub(super) fn prohibit_or_lint_bare_trait_object_ty(
17+
&self,
18+
self_ty: &hir::Ty<'_>,
19+
in_path: bool,
20+
) {
21+
let tcx = self.tcx();
22+
23+
let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
24+
self_ty.kind
25+
else {
26+
return;
27+
};
28+
29+
let needs_bracket = in_path
30+
&& !tcx
31+
.sess
32+
.source_map()
33+
.span_to_prev_source(self_ty.span)
34+
.ok()
35+
.is_some_and(|s| s.trim_end().ends_with('<'));
36+
37+
let is_global = poly_trait_ref.trait_ref.path.is_global();
38+
39+
let mut sugg = vec![(
40+
self_ty.span.shrink_to_lo(),
41+
format!(
42+
"{}dyn {}",
43+
if needs_bracket { "<" } else { "" },
44+
if is_global { "(" } else { "" },
45+
),
46+
)];
47+
48+
if is_global || needs_bracket {
49+
sugg.push((
50+
self_ty.span.shrink_to_hi(),
51+
format!(
52+
"{}{}",
53+
if is_global { ")" } else { "" },
54+
if needs_bracket { ">" } else { "" },
55+
),
56+
));
57+
}
58+
59+
if self_ty.span.edition().at_least_rust_2021() {
60+
let msg = "trait objects must include the `dyn` keyword";
61+
let label = "add `dyn` keyword before this trait";
62+
let mut diag =
63+
rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
64+
if self_ty.span.can_be_used_for_suggestions()
65+
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
66+
{
67+
// FIXME: Only emit this suggestion if the trait is object safe.
68+
diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
69+
}
70+
// Check if the impl trait that we are considering is an impl of a local trait.
71+
self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag);
72+
self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag);
73+
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
74+
} else {
75+
let msg = "trait objects without an explicit `dyn` are deprecated";
76+
tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| {
77+
if self_ty.span.can_be_used_for_suggestions() {
78+
lint.multipart_suggestion_verbose(
79+
"if this is an object-safe trait, use `dyn`",
80+
sugg,
81+
Applicability::MachineApplicable,
82+
);
83+
}
84+
self.maybe_suggest_blanket_trait_impl(self_ty, lint);
85+
});
86+
}
87+
}
88+
1289
/// Make sure that we are in the condition to suggest the blanket implementation.
13-
pub(super) fn maybe_lint_blanket_trait_impl<G: EmissionGuarantee>(
90+
fn maybe_suggest_blanket_trait_impl<G: EmissionGuarantee>(
1491
&self,
1592
self_ty: &hir::Ty<'_>,
1693
diag: &mut Diag<'_, G>,
@@ -75,9 +152,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
75152
}
76153

77154
/// Make sure that we are in the condition to suggest `impl Trait`.
78-
fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool {
155+
fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool {
79156
let tcx = self.tcx();
80157
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
158+
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
159+
// and suggest `Trait0<Ty = impl Trait1>`.
81160
let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) {
82161
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => {
83162
(sig, generics, None)
@@ -186,71 +265,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
186265
false
187266
}
188267

189-
pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
190-
let tcx = self.tcx();
191-
if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
192-
self_ty.kind
193-
{
194-
let needs_bracket = in_path
195-
&& !tcx
196-
.sess
197-
.source_map()
198-
.span_to_prev_source(self_ty.span)
199-
.ok()
200-
.is_some_and(|s| s.trim_end().ends_with('<'));
201-
202-
let is_global = poly_trait_ref.trait_ref.path.is_global();
203-
204-
let mut sugg = Vec::from_iter([(
205-
self_ty.span.shrink_to_lo(),
206-
format!(
207-
"{}dyn {}",
208-
if needs_bracket { "<" } else { "" },
209-
if is_global { "(" } else { "" },
210-
),
211-
)]);
268+
fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) {
269+
let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id);
212270

213-
if is_global || needs_bracket {
214-
sugg.push((
215-
self_ty.span.shrink_to_hi(),
216-
format!(
217-
"{}{}",
218-
if is_global { ")" } else { "" },
219-
if needs_bracket { ">" } else { "" },
220-
),
221-
));
271+
if let Some((_, hir::Node::TypeBinding(binding))) = parents.next()
272+
&& let hir::TypeBindingKind::Equality { term: hir::Term::Ty(obj_ty) } = binding.kind
273+
{
274+
if let Some((_, hir::Node::TraitRef(..))) = parents.next()
275+
&& let Some((_, hir::Node::Ty(ty))) = parents.next()
276+
&& let hir::TyKind::TraitObject(..) = ty.kind
277+
{
278+
// Assoc ty bounds aren't permitted inside trait object types.
279+
return;
222280
}
223281

224-
if self_ty.span.edition().at_least_rust_2021() {
225-
let msg = "trait objects must include the `dyn` keyword";
226-
let label = "add `dyn` keyword before this trait";
227-
let mut diag =
228-
rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
229-
if self_ty.span.can_be_used_for_suggestions()
230-
&& !self.maybe_lint_impl_trait(self_ty, &mut diag)
231-
{
232-
diag.multipart_suggestion_verbose(
233-
label,
234-
sugg,
235-
Applicability::MachineApplicable,
236-
);
237-
}
238-
// check if the impl trait that we are considering is a impl of a local trait
239-
self.maybe_lint_blanket_trait_impl(self_ty, &mut diag);
240-
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
282+
let lo = if binding.gen_args.span_ext.is_dummy() {
283+
binding.ident.span
241284
} else {
242-
let msg = "trait objects without an explicit `dyn` are deprecated";
243-
tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| {
244-
if self_ty.span.can_be_used_for_suggestions() {
245-
lint.multipart_suggestion_verbose(
246-
"if this is an object-safe trait, use `dyn`",
247-
sugg,
248-
Applicability::MachineApplicable,
249-
);
250-
}
251-
self.maybe_lint_blanket_trait_impl(self_ty, lint);
252-
});
285+
binding.gen_args.span_ext
286+
};
287+
let hi = obj_ty.span;
288+
289+
if !lo.eq_ctxt(hi) {
290+
return;
253291
}
292+
293+
diag.span_suggestion_verbose(
294+
lo.between(hi),
295+
"you might have meant to write a bound here",
296+
": ",
297+
Applicability::MaybeIncorrect,
298+
);
254299
}
255300
}
256301
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2339,12 +2339,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
23392339
)
23402340
}
23412341
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
2342-
self.maybe_lint_bare_trait(hir_ty, in_path);
2342+
self.prohibit_or_lint_bare_trait_object_ty(hir_ty, in_path);
2343+
23432344
let repr = match repr {
23442345
TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
23452346
TraitObjectSyntax::DynStar => ty::DynStar,
23462347
};
2347-
23482348
self.lower_trait_object_ty(
23492349
hir_ty.span,
23502350
hir_ty.hir_id,

compiler/rustc_parse/messages.ftl

+6-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ parse_array_index_offset_of = array indexing not supported in offset_of
1414
1515
parse_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is not allowed
1616
17-
parse_assoc_lifetime = associated lifetimes are not supported
18-
.label = the lifetime is given here
19-
.help = if you meant to specify a trait object, write `dyn Trait + 'lifetime`
20-
2117
parse_associated_static_item_not_allowed = associated `static` items are not allowed
2218
2319
parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or later
@@ -445,6 +441,12 @@ parse_lifetime_in_borrow_expression = borrow expressions cannot be annotated wit
445441
.suggestion = remove the lifetime annotation
446442
.label = annotated with lifetime here
447443
444+
parse_lifetime_in_eq_constraint = lifetimes are not permitted in this context
445+
.label = lifetime is not allowed here
446+
.context_label = this introduces an associated item binding
447+
.help = if you meant to specify a trait object, write `dyn /* Trait */ + {$lifetime}`
448+
.colon_sugg = you might have meant to write a bound here
449+
448450
parse_lone_slash = invalid trailing slash in literal
449451
.label = {parse_lone_slash}
450452

compiler/rustc_parse/src/errors.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -2631,13 +2631,22 @@ pub(crate) struct GenericsInPath {
26312631
}
26322632

26332633
#[derive(Diagnostic)]
2634-
#[diag(parse_assoc_lifetime)]
2634+
#[diag(parse_lifetime_in_eq_constraint)]
26352635
#[help]
2636-
pub(crate) struct AssocLifetime {
2636+
pub(crate) struct LifetimeInEqConstraint {
26372637
#[primary_span]
2638-
pub span: Span,
26392638
#[label]
2640-
pub lifetime: Span,
2639+
pub span: Span,
2640+
pub lifetime: Ident,
2641+
#[label(parse_context_label)]
2642+
pub binding_label: Span,
2643+
#[suggestion(
2644+
parse_colon_sugg,
2645+
style = "verbose",
2646+
applicability = "maybe-incorrect",
2647+
code = ": "
2648+
)]
2649+
pub colon_sugg: Span,
26412650
}
26422651

26432652
#[derive(Diagnostic)]

compiler/rustc_parse/src/parser/path.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,11 @@ impl<'a> Parser<'a> {
718718
let bounds = self.parse_generic_bounds()?;
719719
AssocConstraintKind::Bound { bounds }
720720
} else if self.eat(&token::Eq) {
721-
self.parse_assoc_equality_term(ident, self.prev_token.span)?
721+
self.parse_assoc_equality_term(
722+
ident,
723+
gen_args.as_ref(),
724+
self.prev_token.span,
725+
)?
722726
} else {
723727
unreachable!();
724728
};
@@ -753,11 +757,13 @@ impl<'a> Parser<'a> {
753757
}
754758

755759
/// Parse the term to the right of an associated item equality constraint.
756-
/// That is, parse `<term>` in `Item = <term>`.
757-
/// Right now, this only admits types in `<term>`.
760+
///
761+
/// That is, parse `$term` in `Item = $term` where `$term` is a type or
762+
/// a const expression (wrapped in curly braces if complex).
758763
fn parse_assoc_equality_term(
759764
&mut self,
760765
ident: Ident,
766+
gen_args: Option<&GenericArgs>,
761767
eq: Span,
762768
) -> PResult<'a, AssocConstraintKind> {
763769
let arg = self.parse_generic_arg(None)?;
@@ -769,9 +775,15 @@ impl<'a> Parser<'a> {
769775
c.into()
770776
}
771777
Some(GenericArg::Lifetime(lt)) => {
772-
let guar =
773-
self.dcx().emit_err(errors::AssocLifetime { span, lifetime: lt.ident.span });
774-
self.mk_ty(span, ast::TyKind::Err(guar)).into()
778+
let guar = self.dcx().emit_err(errors::LifetimeInEqConstraint {
779+
span: lt.ident.span,
780+
lifetime: lt.ident,
781+
binding_label: span,
782+
colon_sugg: gen_args
783+
.map_or(ident.span, |args| args.span())
784+
.between(lt.ident.span),
785+
});
786+
self.mk_ty(lt.ident.span, ast::TyKind::Err(guar)).into()
775787
}
776788
None => {
777789
let after_eq = eq.shrink_to_hi();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Regression test for issue #105056.
2+
//@ edition: 2021
3+
4+
fn f(_: impl Trait<T = Copy>) {}
5+
//~^ ERROR trait objects must include the `dyn` keyword
6+
//~| HELP add `dyn` keyword before this trait
7+
//~| HELP you might have meant to write a bound here
8+
//~| ERROR the trait `Copy` cannot be made into an object
9+
10+
fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {}
11+
//~^ ERROR trait objects must include the `dyn` keyword
12+
//~| HELP add `dyn` keyword before this trait
13+
//~| HELP you might have meant to write a bound here
14+
//~| ERROR only auto traits can be used as additional traits in a trait object
15+
//~| HELP consider creating a new trait
16+
//~| ERROR the trait `Eq` cannot be made into an object
17+
18+
fn h(_: impl Trait<T<> = 'static + for<'a> Fn(&'a ())>) {}
19+
//~^ ERROR trait objects must include the `dyn` keyword
20+
//~| HELP add `dyn` keyword before this trait
21+
//~| HELP you might have meant to write a bound here
22+
23+
// Don't suggest assoc ty bound in trait object types, that's not valid:
24+
type Obj = dyn Trait<T = Clone>;
25+
//~^ ERROR trait objects must include the `dyn` keyword
26+
//~| HELP add `dyn` keyword before this trait
27+
28+
trait Trait { type T; }
29+
30+
fn main() {}

0 commit comments

Comments
 (0)