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

Ban associated type bounds in bad positions #108063

Merged
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
3 changes: 3 additions & 0 deletions compiler/rustc_ast_lowering/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ ast_lowering_remove_parentheses = remove these parentheses
ast_lowering_misplaced_impl_trait =
`impl Trait` only allowed in function and inherent method return types, not in {$position}

ast_lowering_misplaced_assoc_ty_binding =
associated type bounds are only allowed in where clauses and function signatures, not in {$position}

ast_lowering_rustc_box_attribute_error =
#[rustc_box] requires precisely one argument and no other attributes are allowed

Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ pub struct MisplacedImplTrait<'a> {
pub position: DiagnosticArgFromDisplay<'a>,
}

#[derive(Diagnostic)]
#[diag(ast_lowering_misplaced_assoc_ty_binding)]
pub struct MisplacedAssocTyBinding<'a> {
#[primary_span]
pub span: Span,
pub position: DiagnosticArgFromDisplay<'a>,
}

#[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_rustc_box_attribute_error)]
pub struct RustcBoxAttributeError {
Expand Down
135 changes: 73 additions & 62 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,31 +288,31 @@ enum ImplTraitPosition {
impl std::fmt::Display for ImplTraitPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
ImplTraitPosition::Path => "path",
ImplTraitPosition::Variable => "variable binding",
ImplTraitPosition::Trait => "trait",
ImplTraitPosition::AsyncBlock => "async block",
ImplTraitPosition::Bound => "bound",
ImplTraitPosition::Generic => "generic",
ImplTraitPosition::ExternFnParam => "`extern fn` param",
ImplTraitPosition::ClosureParam => "closure param",
ImplTraitPosition::PointerParam => "`fn` pointer param",
ImplTraitPosition::FnTraitParam => "`Fn` trait param",
ImplTraitPosition::TraitParam => "trait method param",
ImplTraitPosition::ImplParam => "`impl` method param",
ImplTraitPosition::ExternFnReturn => "`extern fn` return",
ImplTraitPosition::ClosureReturn => "closure return",
ImplTraitPosition::PointerReturn => "`fn` pointer return",
ImplTraitPosition::FnTraitReturn => "`Fn` trait return",
ImplTraitPosition::TraitReturn => "trait method return",
ImplTraitPosition::ImplReturn => "`impl` method return",
ImplTraitPosition::GenericDefault => "generic parameter default",
ImplTraitPosition::ConstTy => "const type",
ImplTraitPosition::StaticTy => "static type",
ImplTraitPosition::AssocTy => "associated type",
ImplTraitPosition::FieldTy => "field type",
ImplTraitPosition::Cast => "cast type",
ImplTraitPosition::ImplSelf => "impl header",
ImplTraitPosition::Path => "paths",
ImplTraitPosition::Variable => "variable bindings",
ImplTraitPosition::Trait => "traits",
ImplTraitPosition::AsyncBlock => "async blocks",
ImplTraitPosition::Bound => "bounds",
ImplTraitPosition::Generic => "generics",
ImplTraitPosition::ExternFnParam => "`extern fn` params",
ImplTraitPosition::ClosureParam => "closure params",
ImplTraitPosition::PointerParam => "`fn` pointer params",
ImplTraitPosition::FnTraitParam => "`Fn` trait params",
ImplTraitPosition::TraitParam => "trait method params",
ImplTraitPosition::ImplParam => "`impl` method params",
ImplTraitPosition::ExternFnReturn => "`extern fn` return types",
ImplTraitPosition::ClosureReturn => "closure return types",
ImplTraitPosition::PointerReturn => "`fn` pointer return types",
ImplTraitPosition::FnTraitReturn => "`Fn` trait return types",
ImplTraitPosition::TraitReturn => "trait method return types",
ImplTraitPosition::ImplReturn => "`impl` method return types",
ImplTraitPosition::GenericDefault => "generic parameter defaults",
ImplTraitPosition::ConstTy => "const types",
ImplTraitPosition::StaticTy => "static types",
ImplTraitPosition::AssocTy => "associated types",
ImplTraitPosition::FieldTy => "field types",
ImplTraitPosition::Cast => "cast types",
ImplTraitPosition::ImplSelf => "impl headers",
};

write!(f, "{name}")
Expand Down Expand Up @@ -1002,8 +1002,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
self.arena.alloc(hir::GenericArgs::none())
};
let itctx_tait = &ImplTraitContext::TypeAliasesOpaqueTy;

let kind = match &constraint.kind {
AssocConstraintKind::Equality { term } => {
let term = match term {
Expand All @@ -1013,8 +1011,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TypeBindingKind::Equality { term }
}
AssocConstraintKind::Bound { bounds } => {
enum DesugarKind<'a> {
ImplTrait,
Error(&'a ImplTraitPosition),
Bound,
}

// Piggy-back on the `impl Trait` context to figure out the correct behavior.
let (desugar_to_impl_trait, itctx) = match itctx {
let desugar_kind = match itctx {
// We are in the return position:
//
// fn foo() -> impl Iterator<Item: Debug>
Expand All @@ -1023,7 +1027,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
//
// fn foo() -> impl Iterator<Item = impl Debug>
ImplTraitContext::ReturnPositionOpaqueTy { .. }
| ImplTraitContext::TypeAliasesOpaqueTy { .. } => (true, itctx),
| ImplTraitContext::TypeAliasesOpaqueTy { .. } => DesugarKind::ImplTrait,

// We are in the argument position, but within a dyn type:
//
Expand All @@ -1032,15 +1036,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// so desugar to
//
// fn foo(x: dyn Iterator<Item = impl Debug>)
ImplTraitContext::Universal if self.is_in_dyn_type => (true, itctx),
ImplTraitContext::Universal if self.is_in_dyn_type => DesugarKind::ImplTrait,

// In `type Foo = dyn Iterator<Item: Debug>` we desugar to
// `type Foo = dyn Iterator<Item = impl Debug>` but we have to override the
// "impl trait context" to permit `impl Debug` in this position (it desugars
// then to an opaque type).
//
// FIXME: this is only needed until `impl Trait` is allowed in type aliases.
ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => (true, itctx_tait),
ImplTraitContext::Disallowed(position) if self.is_in_dyn_type => {
DesugarKind::Error(position)
}

// We are in the parameter position, but not within a dyn type:
//
Expand All @@ -1049,35 +1049,46 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// so we leave it as is and this gets expanded in astconv to a bound like
// `<T as Iterator>::Item: Debug` where `T` is the type parameter for the
// `impl Iterator`.
_ => (false, itctx),
_ => DesugarKind::Bound,
};

if desugar_to_impl_trait {
// Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`. We do this by
// constructing the HIR for `impl bounds...` and then lowering that.

let impl_trait_node_id = self.next_node_id();

self.with_dyn_type_scope(false, |this| {
let node_id = this.next_node_id();
let ty = this.lower_ty(
&Ty {
id: node_id,
kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
span: this.lower_span(constraint.span),
tokens: None,
},
itctx,
);
match desugar_kind {
DesugarKind::ImplTrait => {
// Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`. We do this by
// constructing the HIR for `impl bounds...` and then lowering that.

hir::TypeBindingKind::Equality { term: ty.into() }
})
} else {
// Desugar `AssocTy: Bounds` into a type binding where the
// later desugars into a trait predicate.
let bounds = self.lower_param_bounds(bounds, itctx);
let impl_trait_node_id = self.next_node_id();

hir::TypeBindingKind::Constraint { bounds }
self.with_dyn_type_scope(false, |this| {
let node_id = this.next_node_id();
let ty = this.lower_ty(
&Ty {
id: node_id,
kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
span: this.lower_span(constraint.span),
tokens: None,
},
itctx,
);

hir::TypeBindingKind::Equality { term: ty.into() }
})
}
DesugarKind::Bound => {
// Desugar `AssocTy: Bounds` into a type binding where the
// later desugars into a trait predicate.
let bounds = self.lower_param_bounds(bounds, itctx);

hir::TypeBindingKind::Constraint { bounds }
}
DesugarKind::Error(position) => {
self.tcx.sess.emit_err(errors::MisplacedAssocTyBinding {
span: constraint.span,
position: DiagnosticArgFromDisplay(position),
});
let err_ty = &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err));
hir::TypeBindingKind::Equality { term: err_ty.into() }
}
}
}
};
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_ast_passes/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ ast_passes_forbidden_let_stable =
ast_passes_deprecated_where_clause_location =
where clause not allowed here

ast_passes_forbidden_assoc_constraint =
associated type bounds are not allowed within structs, enums, or unions

ast_passes_keyword_lifetime =
lifetimes cannot use keyword names

Expand Down
33 changes: 1 addition & 32 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ struct AstValidator<'a> {
/// or `Foo::Bar<impl Trait>`
is_impl_trait_banned: bool,

/// Used to ban associated type bounds (i.e., `Type<AssocType: Bounds>`) in
/// certain positions.
is_assoc_ty_bound_banned: bool,

/// See [ForbiddenLetReason]
forbidden_let_reason: Option<ForbiddenLetReason>,

Expand Down Expand Up @@ -180,30 +176,12 @@ impl<'a> AstValidator<'a> {
}
}

fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
f(self);
self.is_assoc_ty_bound_banned = old;
}

fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.outer_impl_trait, outer);
f(self);
self.outer_impl_trait = old;
}

fn visit_assoc_constraint_from_generic_args(&mut self, constraint: &'a AssocConstraint) {
match constraint.kind {
AssocConstraintKind::Equality { .. } => {}
AssocConstraintKind::Bound { .. } => {
if self.is_assoc_ty_bound_banned {
self.session.emit_err(ForbiddenAssocConstraint { span: constraint.span });
}
}
}
self.visit_assoc_constraint(constraint);
}

// Mirrors `visit::walk_ty`, but tracks relevant state.
fn walk_ty(&mut self, t: &'a Ty) {
match &t.kind {
Expand Down Expand Up @@ -1248,7 +1226,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// are allowed to contain nested `impl Trait`.
AngleBracketedArg::Constraint(constraint) => {
self.with_impl_trait(None, |this| {
this.visit_assoc_constraint_from_generic_args(constraint);
this.visit_assoc_constraint(constraint);
});
}
}
Expand Down Expand Up @@ -1373,14 +1351,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_param_bound(self, bound)
}

fn visit_variant_data(&mut self, s: &'a VariantData) {
self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
}

fn visit_enum_def(&mut self, enum_definition: &'a EnumDef) {
self.with_banned_assoc_ty_bound(|this| visit::walk_enum_def(this, enum_definition))
}

fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) {
// Only associated `fn`s can have `self` parameters.
let self_semantic = match fk.ctxt() {
Expand Down Expand Up @@ -1709,7 +1679,6 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
outer_impl_trait: None,
disallow_tilde_const: None,
is_impl_trait_banned: false,
is_assoc_ty_bound_banned: false,
forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden),
lint_buffer: lints,
};
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@ pub struct ForbiddenLetStable {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_forbidden_assoc_constraint)]
pub struct ForbiddenAssocConstraint {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_keyword_lifetime)]
pub struct KeywordLifetime {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-consts/issue-105330.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ LL | fn main<A: TraitWAssocConst<A=32>>() {
= note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
= help: add `#![feature(associated_const_equality)]` to the crate attributes to enable

error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in impl header
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in impl headers
--> $DIR/issue-105330.rs:6:27
|
LL | impl TraitWAssocConst for impl Demo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![feature(associated_type_bounds)]

trait B {
type AssocType;
}

fn f()
where
dyn for<'j> B<AssocType: 'j>:,
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
{
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: associated type bounds are only allowed in where clauses and function signatures, not in bounds
--> $DIR/bad-universal-in-dyn-in-where-clause.rs:9:19
|
LL | dyn for<'j> B<AssocType: 'j>:,
| ^^^^^^^^^^^^^

error: aborting due to previous error

13 changes: 13 additions & 0 deletions tests/ui/associated-type-bounds/bad-universal-in-impl-sig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(associated_type_bounds)]

trait Trait {
type Item;
}

trait Trait2 {}

// It's not possible to insert a universal `impl Trait` here!
impl dyn Trait<Item: Trait2> {}
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: associated type bounds are only allowed in where clauses and function signatures, not in impl headers
--> $DIR/bad-universal-in-impl-sig.rs:10:16
|
LL | impl dyn Trait<Item: Trait2> {}
| ^^^^^^^^^^^^

error: aborting due to previous error

22 changes: 9 additions & 13 deletions tests/ui/associated-type-bounds/inside-adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,24 @@
use std::mem::ManuallyDrop;

struct S1 { f: dyn Iterator<Item: Copy> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
struct S2 { f: Box<dyn Iterator<Item: Copy>> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
struct S3 { f: dyn Iterator<Item: 'static> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures

enum E1 { V(dyn Iterator<Item: Copy>) }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)`
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
enum E2 { V(Box<dyn Iterator<Item: Copy>>) }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
enum E3 { V(dyn Iterator<Item: 'static>) }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized + 'static> + 'static)`
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures

union U1 { f: ManuallyDrop<dyn Iterator<Item: Copy>> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)`
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
union U2 { f: ManuallyDrop<Box<dyn Iterator<Item: Copy>>> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures
union U3 { f: ManuallyDrop<dyn Iterator<Item: 'static>> }
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized + 'static> + 'static)`
//~^ ERROR associated type bounds are only allowed in where clauses and function signatures

fn main() {}
Loading