From 884af362f2d1eeda44af46443d9f78037078192f Mon Sep 17 00:00:00 2001 From: Raekye Date: Wed, 27 Sep 2023 17:25:31 -0400 Subject: [PATCH] Validate `~const` trait bounds on associated fns. Previously, any associated function could have `~const` trait bounds on generic parameters, which could lead to ICEs when these bounds were used on associated functions of non-`#[const_trait] trait` or non-`impl const` blocks. Includes changes as per @fee1-dead's comments in #116210. --- .../rustc_ast_passes/src/ast_validation.rs | 56 +++++++++++-------- .../const-bound-on-not-const-associated-fn.rs | 28 ++++++++++ ...st-bound-on-not-const-associated-fn.stderr | 26 +++++++++ .../tilde-const-and-const-params.rs | 9 +-- .../tilde-const-and-const-params.stderr | 20 +++++-- .../trait-where-clause.rs | 2 + .../trait-where-clause.stderr | 32 +++++++++-- 7 files changed, 139 insertions(+), 34 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 7bc685a5450..743fad8e865 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -52,7 +52,8 @@ struct AstValidator<'a> { /// Are we inside a trait impl? in_trait_impl: bool, - in_const_trait_impl: bool, + /// Are we inside a const trait defn or impl? + in_const_trait_or_impl: bool, has_proc_macro_decls: bool, @@ -78,11 +79,19 @@ fn with_in_trait_impl( f: impl FnOnce(&mut Self), ) { let old = mem::replace(&mut self.in_trait_impl, is_in); - let old_const = - mem::replace(&mut self.in_const_trait_impl, matches!(constness, Some(Const::Yes(_)))); + let old_const = mem::replace( + &mut self.in_const_trait_or_impl, + matches!(constness, Some(Const::Yes(_))), + ); f(self); self.in_trait_impl = old; - self.in_const_trait_impl = old_const; + self.in_const_trait_or_impl = old_const; + } + + fn with_in_trait(&mut self, is_const: bool, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.in_const_trait_or_impl, is_const); + f(self); + self.in_const_trait_or_impl = old; } fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { @@ -933,23 +942,26 @@ fn visit_item(&mut self, item: &'a Item) { } } ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => { - if *is_auto == IsAuto::Yes { - // Auto traits cannot have generics, super traits nor contain items. - self.deny_generic_params(generics, item.ident.span); - self.deny_super_traits(bounds, item.ident.span); - self.deny_where_clause(&generics.where_clause, item.ident.span); - self.deny_items(items, item.ident.span); - } + let is_const_trait = attr::contains_name(&item.attrs, sym::const_trait); + self.with_in_trait(is_const_trait, |this| { + if *is_auto == IsAuto::Yes { + // Auto traits cannot have generics, super traits nor contain items. + this.deny_generic_params(generics, item.ident.span); + this.deny_super_traits(bounds, item.ident.span); + this.deny_where_clause(&generics.where_clause, item.ident.span); + this.deny_items(items, item.ident.span); + } - // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound - // context for the supertraits. - self.visit_vis(&item.vis); - self.visit_ident(item.ident); - self.visit_generics(generics); - self.with_tilde_const_allowed(|this| { - walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) + // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound + // context for the supertraits. + this.visit_vis(&item.vis); + this.visit_ident(item.ident); + this.visit_generics(generics); + this.with_tilde_const_allowed(|this| { + walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) + }); + walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); }); - walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait); walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again } @@ -1278,7 +1290,7 @@ fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) { let tilde_const_allowed = matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. })) - || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_))); + || matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl); let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk)); @@ -1363,7 +1375,7 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { walk_list!(self, visit_ty, ty); } AssocItemKind::Fn(box Fn { sig, generics, body, .. }) - if self.in_const_trait_impl + if self.in_const_trait_or_impl || ctxt == AssocCtxt::Trait || matches!(sig.header.constness, Const::Yes(_)) => { @@ -1510,7 +1522,7 @@ pub fn check_crate( features, extern_mod: None, in_trait_impl: false, - in_const_trait_impl: false, + in_const_trait_or_impl: false, has_proc_macro_decls: false, outer_impl_trait: None, disallow_tilde_const: None, diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.rs new file mode 100644 index 00000000000..1e22ddcea8d --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.rs @@ -0,0 +1,28 @@ +#![feature(const_trait_impl, effects)] + +#[const_trait] +trait MyTrait { + fn do_something(&self); +} + +trait OtherTrait { + fn do_something_else() where Self: ~const MyTrait; + //~^ ERROR `~const` is not allowed here +} + +struct MyStruct(T); + +impl const MyTrait for u32 { + fn do_something(&self) {} +} + +impl MyStruct { + pub fn foo(&self) where T: ~const MyTrait { + //~^ ERROR `~const` is not allowed here + self.0.do_something(); + } +} + +fn main() { + MyStruct(0u32).foo(); +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.stderr new file mode 100644 index 00000000000..9210f642706 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/const-bound-on-not-const-associated-fn.stderr @@ -0,0 +1,26 @@ +error: `~const` is not allowed here + --> $DIR/const-bound-on-not-const-associated-fn.rs:9:40 + | +LL | fn do_something_else() where Self: ~const MyTrait; + | ^^^^^^^^^^^^^^ + | +note: this function is not `const`, so it cannot have `~const` trait bounds + --> $DIR/const-bound-on-not-const-associated-fn.rs:9:8 + | +LL | fn do_something_else() where Self: ~const MyTrait; + | ^^^^^^^^^^^^^^^^^ + +error: `~const` is not allowed here + --> $DIR/const-bound-on-not-const-associated-fn.rs:20:32 + | +LL | pub fn foo(&self) where T: ~const MyTrait { + | ^^^^^^^^^^^^^^ + | +note: this function is not `const`, so it cannot have `~const` trait bounds + --> $DIR/const-bound-on-not-const-associated-fn.rs:20:12 + | +LL | pub fn foo(&self) where T: ~const MyTrait { + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs index 89d74cecfdb..4b720b534a4 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs @@ -7,7 +7,8 @@ impl Foo { fn add(self) -> Foo<{ A::add(N) }> { - //~^ ERROR mismatched types + //~^ ERROR `~const` is not allowed here + //~| ERROR mismatched types Foo } } @@ -30,7 +31,7 @@ fn bar(_: Foo) -> Foo<{ A::add(N) }> { } fn main() { - let foo = Foo::<0>; - let foo = bar::<(), _>(foo); - let _foo = bar::<(), _>(foo); + let foo = Foo::<0>; + let foo = bar::<(), _>(foo); + let _foo = bar::<(), _>(foo); } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr index ec5d21d33c6..be7a83dc184 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr @@ -1,17 +1,29 @@ error: `~const` is not allowed here - --> $DIR/tilde-const-and-const-params.rs:26:11 + --> $DIR/tilde-const-and-const-params.rs:9:15 + | +LL | fn add(self) -> Foo<{ A::add(N) }> { + | ^^^^^^^^^^^^ + | +note: this function is not `const`, so it cannot have `~const` trait bounds + --> $DIR/tilde-const-and-const-params.rs:9:8 + | +LL | fn add(self) -> Foo<{ A::add(N) }> { + | ^^^ + +error: `~const` is not allowed here + --> $DIR/tilde-const-and-const-params.rs:27:11 | LL | fn bar(_: Foo) -> Foo<{ A::add(N) }> { | ^^^^^^^^^^^^ | note: this function is not `const`, so it cannot have `~const` trait bounds - --> $DIR/tilde-const-and-const-params.rs:26:4 + --> $DIR/tilde-const-and-const-params.rs:27:4 | LL | fn bar(_: Foo) -> Foo<{ A::add(N) }> { | ^^^ error[E0308]: mismatched types - --> $DIR/tilde-const-and-const-params.rs:26:61 + --> $DIR/tilde-const-and-const-params.rs:27:61 | LL | fn bar(_: Foo) -> Foo<{ A::add(N) }> { | ^^^^^^^^^ expected `false`, found `true` @@ -28,6 +40,6 @@ LL | fn add(self) -> Foo<{ A::add(N) }> { = note: expected constant `false` found constant `true` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs index 85ca5fc9048..11f353f3f8a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs @@ -6,7 +6,9 @@ trait Bar {} trait Foo { fn a(); fn b() where Self: ~const Bar; + //~^ ERROR `~const` is not allowed here fn c(); + //~^ ERROR `~const` is not allowed here } fn test1() { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr index 255878e1775..3d6fedbabbf 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr @@ -1,5 +1,29 @@ +error: `~const` is not allowed here + --> $DIR/trait-where-clause.rs:8:24 + | +LL | fn b() where Self: ~const Bar; + | ^^^^^^^^^^ + | +note: this function is not `const`, so it cannot have `~const` trait bounds + --> $DIR/trait-where-clause.rs:8:8 + | +LL | fn b() where Self: ~const Bar; + | ^ + +error: `~const` is not allowed here + --> $DIR/trait-where-clause.rs:10:13 + | +LL | fn c(); + | ^^^^^^^^^^ + | +note: this function is not `const`, so it cannot have `~const` trait bounds + --> $DIR/trait-where-clause.rs:10:8 + | +LL | fn c(); + | ^ + error[E0277]: the trait bound `T: Bar` is not satisfied - --> $DIR/trait-where-clause.rs:14:5 + --> $DIR/trait-where-clause.rs:16:5 | LL | T::b(); | ^ the trait `Bar` is not implemented for `T` @@ -15,13 +39,13 @@ LL | fn test1() { | +++++ error[E0277]: the trait bound `T: Bar` is not satisfied - --> $DIR/trait-where-clause.rs:16:12 + --> $DIR/trait-where-clause.rs:18:12 | LL | T::c::(); | ^ the trait `Bar` is not implemented for `T` | note: required by a bound in `Foo::c` - --> $DIR/trait-where-clause.rs:9:13 + --> $DIR/trait-where-clause.rs:10:13 | LL | fn c(); | ^^^^^^^^^^ required by this bound in `Foo::c` @@ -30,6 +54,6 @@ help: consider further restricting this bound LL | fn test1() { | +++++ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`.