Skip to content

Commit

Permalink
Deny more ~const trait bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Nov 11, 2023
1 parent 9ab0749 commit e345737
Show file tree
Hide file tree
Showing 25 changed files with 500 additions and 133 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,12 @@ ast_passes_static_without_body =
.suggestion = provide a definition for the static
ast_passes_tilde_const_disallowed = `~const` is not allowed here
.trait = trait objects cannot have `~const` trait bounds
.closure = closures cannot have `~const` trait bounds
.function = this function is not `const`, so it cannot have `~const` trait bounds
.trait = this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds
.impl = this impl is not `const`, so it cannot have `~const` trait bounds
.object = trait objects cannot have `~const` trait bounds
.item = this item cannot have `~const` trait bounds
ast_passes_trait_fn_const =
functions in traits cannot be declared const
Expand Down
130 changes: 86 additions & 44 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ enum SelfSemantic {
enum DisallowTildeConstContext<'a> {
TraitObject,
Fn(FnKind<'a>),
Trait(Span),
Impl(Span),
Item,
}

struct AstValidator<'a> {
Expand Down Expand Up @@ -110,10 +113,6 @@ impl<'a> AstValidator<'a> {
self.disallow_tilde_const = old;
}

fn with_tilde_const_allowed(&mut self, f: impl FnOnce(&mut Self)) {
self.with_tilde_const(None, f)
}

fn with_banned_tilde_const(
&mut self,
ctx: DisallowTildeConstContext<'a>,
Expand Down Expand Up @@ -822,11 +821,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

this.visit_vis(&item.vis);
this.visit_ident(item.ident);
if let Const::Yes(_) = constness {
this.with_tilde_const_allowed(|this| this.visit_generics(generics));
} else {
this.visit_generics(generics);
}
let disallowed = matches!(constness, Const::No)
.then(|| DisallowTildeConstContext::Impl(item.span));
this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
this.visit_trait_ref(t);
this.visit_ty(self_ty);

Expand Down Expand Up @@ -926,7 +923,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.extern_mod = old_item;
return; // Avoid visiting again.
}
ItemKind::Enum(def, _) => {
ItemKind::Enum(def, generics) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);

self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics)
});

for variant in &def.variants {
self.visibility_not_permitted(
&variant.vis,
Expand All @@ -939,6 +943,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
);
}
}
walk_list!(self, visit_variant, &def.variants);
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again
}
ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
let is_const_trait = attr::contains_name(&item.attrs, sym::const_trait);
Expand All @@ -955,8 +962,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// context for the supertraits.
this.visit_vis(&item.vis);
this.visit_ident(item.ident);
this.visit_generics(generics);
this.with_tilde_const_allowed(|this| {
let disallowed =
(!is_const_trait).then(|| DisallowTildeConstContext::Trait(item.span));
this.with_tilde_const(disallowed, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
Expand All @@ -975,23 +984,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_mod_file_item_asciionly(item.ident);
}
}
ItemKind::Struct(vdata, generics) => match vdata {
// Duplicating the `Visitor` logic allows catching all cases
// of `Anonymous(Struct, Union)` outside of a field struct or union.
//
// Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
// encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
// it uses `visit_ty_common`, which doesn't contain that specific check.
VariantData::Struct(fields, ..) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
ItemKind::Struct(vdata, generics) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics)
});
walk_list!(self, visit_attribute, &item.attrs);

match vdata {
// Permit `Anon{Struct,Union}` as field type.
VariantData::Struct(fields, ..) => {
walk_list!(self, visit_struct_field_def, fields)
}
_ => self.visit_variant_data(vdata),
}
_ => {}
},
return; // Avoid visiting again-
}
ItemKind::Union(vdata, generics) => {
if vdata.fields().is_empty() {
self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
Expand All @@ -1000,20 +1009,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
VariantData::Struct(fields, ..) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics)
});
// Permit `Anon{Struct,Union}` as field type.
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
}
_ => {}
}
}
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
self.check_defaultness(item.span, *defaultness);
self.session.emit_err(errors::ConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
ItemKind::Const(box ConstItem { defaultness, generics, ty, expr }) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
// FIXME(#117791): Also reject `default` if `expr` is `Some`.
if expr.is_none() {
self.check_defaultness(item.span, *defaultness);
}
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics)
});
self.visit_ty(ty);
if let Some(expr) = expr {
self.visit_expr(expr);
} else {
self.session.emit_err(errors::ConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
}
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
}
ItemKind::Static(box StaticItem { expr: None, .. }) => {
self.session.emit_err(errors::StaticWithoutBody {
Expand All @@ -1022,9 +1049,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
});
}
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
ty_alias @ box TyAlias { defaultness, generics, bounds, where_clauses, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness);
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics)
});
if ty.is_none() {
self.session.emit_err(errors::TyAliasWithoutBody {
span: item.span,
Expand Down Expand Up @@ -1189,15 +1219,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let Some(reason) = &self.disallow_tilde_const =>
{
let reason = match reason {
DisallowTildeConstContext::TraitObject => {
errors::TildeConstReason::TraitObject
}
DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
errors::TildeConstReason::Closure
}
DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
errors::TildeConstReason::Function { ident: ident.span }
}
&DisallowTildeConstContext::Trait(span) => errors::TildeConstReason::Trait { span },
&DisallowTildeConstContext::Impl(span) => errors::TildeConstReason::Impl { span },
DisallowTildeConstContext::TraitObject => {
errors::TildeConstReason::TraitObject
}
DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
};
self.err_handler()
.emit_err(errors::TildeConstDisallowed { span: bound.span(), reason });
Expand Down Expand Up @@ -1305,7 +1338,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl);

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

self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
}

Expand Down Expand Up @@ -1374,15 +1406,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}

match &item.kind {
AssocItemKind::Type(box TyAlias { generics, bounds, ty, .. })
if ctxt == AssocCtxt::Trait =>
{
AssocItemKind::Type(box TyAlias { generics, bounds, ty, .. }) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
walk_list!(self, visit_attribute, &item.attrs);
self.with_tilde_const_allowed(|this| {
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
if ctxt == AssocCtxt::Trait {
walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
}
});
walk_list!(self, visit_ty, ty);
}
Expand All @@ -1403,6 +1435,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
);
self.visit_fn(kind, item.span, item.id);
}
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
walk_list!(self, visit_attribute, &item.attrs);
self.with_banned_tilde_const(DisallowTildeConstContext::Item, |this| {
this.visit_generics(generics);
});
self.visit_ty(ty);
walk_list!(self, visit_expr, expr);
}
_ => self
.with_in_trait_impl(false, None, |this| visit::walk_assoc_item(this, item, ctxt)),
}
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,15 +551,27 @@ pub struct TildeConstDisallowed {

#[derive(Subdiagnostic)]
pub enum TildeConstReason {
#[note(ast_passes_trait)]
TraitObject,
#[note(ast_passes_closure)]
Closure,
#[note(ast_passes_function)]
Function {
#[primary_span]
ident: Span,
},
#[note(ast_passes_trait)]
Trait {
#[primary_span]
span: Span,
},
#[note(ast_passes_impl)]
Impl {
#[primary_span]
span: Span,
},
#[note(ast_passes_object)]
TraitObject,
#[note(ast_passes_item)]
Item,
}

#[derive(Diagnostic)]
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/generic-const-items/const-trait-impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
#![allow(incomplete_features)]
#![crate_type = "lib"]

// FIXME(generic_const_items): Interpret `~const` as always-const.
const CREATE<T: ~const Create>: T = T::create();
// FIXME(generic_const_items, effects): Introduce `const` bounds to make this work.
const CREATE<T: Create>: T = T::create();

pub const K0: i32 = CREATE::<i32>;
pub const K1: i32 = CREATE; // arg inferred
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/generic-const-items/const-trait-impl.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0015]: cannot call non-const fn `<T as Create>::create` in constants
--> $DIR/const-trait-impl.rs:11:37
--> $DIR/const-trait-impl.rs:11:30
|
LL | const CREATE<T: ~const Create>: T = T::create();
| ^^^^^^^^^^^
LL | const CREATE<T: Create>: T = T::create();
| ^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
error: `~const` is not allowed here
--> $DIR/assoc-type-const-bound-usage.rs:7:17
|
LL | type Assoc: ~const Foo;
| ^^^^^^^^^^
|
= note: this item cannot have `~const` trait bounds

error[E0308]: mismatched types
--> $DIR/assoc-type-const-bound-usage.rs:12:5
|
Expand All @@ -7,6 +15,6 @@ LL | <T as Foo>::Assoc::foo();
= note: expected constant `host`
found constant `true`

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

For more information about this error, try `rustc --explain E0308`.
10 changes: 9 additions & 1 deletion tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
error: `~const` is not allowed here
--> $DIR/assoc-type.rs:17:15
|
LL | type Bar: ~const std::ops::Add;
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this item cannot have `~const` trait bounds

error: ~const can only be applied to `#[const_trait]` traits
--> $DIR/assoc-type.rs:17:22
|
LL | type Bar: ~const std::ops::Add;
| ^^^^^^^^^^^^^

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

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(const_trait_impl, effects)]
#![feature(const_trait_impl)]

#[const_trait]
trait MyTrait {
Expand Down
21 changes: 6 additions & 15 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.precise.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
error[E0493]: destructor of `T` cannot be evaluated at compile-time
--> $DIR/const-drop.rs:19:32
error: `~const` is not allowed here
--> $DIR/const-drop.rs:67:38
|
LL | const fn a<T: ~const Destruct>(_: T) {}
| ^ - value is dropped here
| |
| the destructor for this type cannot be evaluated in constant functions

error[E0493]: destructor of `S<'_>` cannot be evaluated at compile-time
--> $DIR/const-drop.rs:24:13
LL | pub struct ConstDropWithBound<T: ~const SomeTrait>(pub core::marker::PhantomData<T>);
| ^^^^^^^^^^^^^^^^
|
LL | let _ = S(&mut c);
| ^^^^^^^^^- value is dropped here
| |
| the destructor for this type cannot be evaluated in constant functions
= note: this item cannot have `~const` trait bounds

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

For more information about this error, try `rustc --explain E0493`.
1 change: 1 addition & 0 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ mod t {
fn foo() {}
}

// FIXME(effects): This should be a `const` bound instead of a `~const` one.
pub struct ConstDropWithBound<T: ~const SomeTrait>(pub core::marker::PhantomData<T>);

impl<T: ~const SomeTrait> const Drop for ConstDropWithBound<T> {
Expand Down
Loading

0 comments on commit e345737

Please sign in to comment.