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

Ensure that ~const trait bounds on associated functions are in const traits or impls #116210

Merged
merged 1 commit into from
Oct 3, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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`.