Skip to content

Commit

Permalink
Validate that ~const trait bounds are not applied to associated
Browse files Browse the repository at this point in the history
functions where it does not make sense to; previously, any associated
function could have `~const` trait bounds on generic paramters, which
lead to ICEs when these bounds were used on associated functions in
non-`impl const` or non-`#[const_trait] trait` blocks.
  • Loading branch information
Raekye committed Sep 27, 2023
1 parent 7b4b1b0 commit d6a6f74
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 17 deletions.
55 changes: 38 additions & 17 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ struct AstValidator<'a> {

in_const_trait_impl: bool,

/// Are we inside a const trait defn?
in_const_trait_defn: bool,

has_proc_macro_decls: bool,

/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
Expand Down Expand Up @@ -85,6 +88,12 @@ impl<'a> AstValidator<'a> {
self.in_const_trait_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_defn, is_const);
f(self);
self.in_const_trait_defn = old;
}

fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.is_impl_trait_banned, true);
f(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 @@ -1277,8 +1289,16 @@ 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(_)));
if matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. })) {
true
} else if let Some(FnCtxt::Assoc(ctxt)) = fk.ctxt() {
match ctxt {
AssocCtxt::Trait => self.in_const_trait_defn,
AssocCtxt::Impl => self.in_const_trait_impl,
}
} else {
false
};

let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk));

Expand Down Expand Up @@ -1511,6 +1531,7 @@ pub fn check_crate(
extern_mod: None,
in_trait_impl: false,
in_const_trait_impl: false,
in_const_trait_defn: 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,33 @@
#![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:11:15
|
LL | 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()
| ^^^^^^^^^^^^^^^^^

error: `~const` is not allowed here
--> $DIR/const-bound-on-not-const-associated-fn.rs:24:12
|
LL | 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:22:12
|
LL | pub fn foo(&self)
| ^^^

error: aborting due to 2 previous errors

0 comments on commit d6a6f74

Please sign in to comment.