Skip to content

Commit

Permalink
Validate ~const trait bounds on associated fns.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Raekye committed Sep 30, 2023
1 parent 2f89c41 commit 884af36
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 34 deletions.
56 changes: 34 additions & 22 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -78,11 +79,19 @@ impl<'a> AstValidator<'a> {
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)) {
Expand Down Expand Up @@ -933,23 +942,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
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
}
Expand Down Expand Up @@ -1278,7 +1290,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

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));

Expand Down Expand Up @@ -1363,7 +1375,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
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(_)) =>
{
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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>(T);

impl const MyTrait for u32 {
fn do_something(&self) {}
}

impl<T> MyStruct<T> {
pub fn foo(&self) where T: ~const MyTrait {
//~^ ERROR `~const` is not allowed here
self.0.do_something();
}
}

fn main() {
MyStruct(0u32).foo();
}
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ struct Foo<const N: usize>;

impl<const N: usize> Foo<N> {
fn add<A: ~const Add42>(self) -> Foo<{ A::add(N) }> {
//~^ ERROR mismatched types
//~^ ERROR `~const` is not allowed here
//~| ERROR mismatched types
Foo
}
}
Expand All @@ -30,7 +31,7 @@ fn bar<A: ~const Add42, const N: usize>(_: Foo<N>) -> 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);
}
Original file line number Diff line number Diff line change
@@ -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<A: ~const Add42>(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<A: ~const Add42>(self) -> Foo<{ A::add(N) }> {
| ^^^

error: `~const` is not allowed here
--> $DIR/tilde-const-and-const-params.rs:27:11
|
LL | fn bar<A: ~const Add42, const N: usize>(_: Foo<N>) -> 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<A: ~const Add42, const N: usize>(_: Foo<N>) -> 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<A: ~const Add42, const N: usize>(_: Foo<N>) -> Foo<{ A::add(N) }> {
| ^^^^^^^^^ expected `false`, found `true`
Expand All @@ -28,6 +40,6 @@ LL | fn add<A: ~const Add42>(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`.
2 changes: 2 additions & 0 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ trait Bar {}
trait Foo {
fn a();
fn b() where Self: ~const Bar;
//~^ ERROR `~const` is not allowed here
fn c<T: ~const Bar>();
//~^ ERROR `~const` is not allowed here
}

fn test1<T: Foo>() {
Expand Down
32 changes: 28 additions & 4 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr
Original file line number Diff line number Diff line change
@@ -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<T: ~const Bar>();
| ^^^^^^^^^^
|
note: this function is not `const`, so it cannot have `~const` trait bounds
--> $DIR/trait-where-clause.rs:10:8
|
LL | fn c<T: ~const Bar>();
| ^

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`
Expand All @@ -15,13 +39,13 @@ LL | fn test1<T: Foo + Bar>() {
| +++++

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::<T>();
| ^ 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<T: ~const Bar>();
| ^^^^^^^^^^ required by this bound in `Foo::c`
Expand All @@ -30,6 +54,6 @@ help: consider further restricting this bound
LL | fn test1<T: Foo + Bar>() {
| +++++

error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 884af36

Please sign in to comment.