Skip to content

Commit

Permalink
Auto merge of #88328 - fee1-dead:not-quite-const, r=oli-obk
Browse files Browse the repository at this point in the history
Introduce `~const`

 - [x] Removed `?const` and change uses of `?const`
 - [x] Added `~const` to the AST. It is gated behind const_trait_impl.
 - [x] Validate `~const` in ast_validation.
 - [x] Update UI Tests
 - [x] Add enum `BoundConstness` (With variants `NotConst` and
 `ConstIfConst` allowing future extensions)
 - [x] Adjust trait selection and pre-existing code to use `BoundConstness`.
 - [ ] Optional steps for this PR
      - [x] Fix #88155
      - [x] ~~Do something with constness bounds in chalk~~ Must be done to rust-lang/chalk (just tried to refactor, there are a lot of errors to resolve :( )
      - [ ] Adjust Error messages for `~const` bounds that can't be satisfied.

r? `@oli-obk`
  • Loading branch information
bors committed Aug 27, 2021
2 parents dfd8472 + 2d7dbf2 commit ac50a53
Show file tree
Hide file tree
Showing 79 changed files with 501 additions and 545 deletions.
6 changes: 3 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ impl ParenthesizedArgs {

pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};

/// A modifier on a bound, e.g., `?Sized` or `?const Trait`.
/// A modifier on a bound, e.g., `?Sized` or `~const Trait`.
///
/// Negative bounds should also be handled here.
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
Expand All @@ -295,10 +295,10 @@ pub enum TraitBoundModifier {
/// `?Trait`
Maybe,

/// `?const Trait`
/// `~const Trait`
MaybeConst,

/// `?const ?Trait`
/// `~const ?Trait`
//
// This parses but will be rejected during AST validation.
MaybeConstMaybe,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ref ty,
TraitBoundModifier::None | TraitBoundModifier::MaybeConst,
) => Some(this.lower_poly_trait_ref(ty, itctx.reborrow())),
// `?const ?Bound` will cause an error during AST validation
// `~const ?Bound` will cause an error during AST validation
// anyways, so treat it like `?Bound` as compilation proceeds.
GenericBound::Trait(
_,
Expand Down
141 changes: 90 additions & 51 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,6 @@ enum SelfSemantic {
No,
}

/// A syntactic context that disallows certain kinds of bounds (e.g., `?Trait` or `?const Trait`).
#[derive(Clone, Copy)]
enum BoundContext {
ImplTrait,
TraitBounds,
TraitObject,
}

impl BoundContext {
fn description(&self) -> &'static str {
match self {
Self::ImplTrait => "`impl Trait`",
Self::TraitBounds => "supertraits",
Self::TraitObject => "trait objects",
}
}
}

struct AstValidator<'a> {
session: &'a Session,

Expand All @@ -60,18 +42,16 @@ struct AstValidator<'a> {
/// Are we inside a trait impl?
in_trait_impl: bool,

in_const_trait_impl: bool,

has_proc_macro_decls: bool,

/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
/// Nested `impl Trait` _is_ allowed in associated type position,
/// e.g., `impl Iterator<Item = impl Debug>`.
outer_impl_trait: Option<Span>,

/// Keeps track of the `BoundContext` as we recurse.
///
/// This is used to forbid `?const Trait` bounds in, e.g.,
/// `impl Iterator<Item = Box<dyn ?const Trait>`.
bound_context: Option<BoundContext>,
is_tilde_const_allowed: bool,

/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
/// or `Foo::Bar<impl Trait>`
Expand All @@ -88,10 +68,18 @@ struct AstValidator<'a> {
}

impl<'a> AstValidator<'a> {
fn with_in_trait_impl(&mut self, is_in: bool, f: impl FnOnce(&mut Self)) {
fn with_in_trait_impl(
&mut self,
is_in: bool,
constness: Option<Const>,
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(_))));
f(self);
self.in_trait_impl = old;
self.in_const_trait_impl = old_const;
}

fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
Expand All @@ -100,6 +88,18 @@ impl<'a> AstValidator<'a> {
self.is_impl_trait_banned = old;
}

fn with_tilde_const_allowed(&mut self, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.is_tilde_const_allowed, true);
f(self);
self.is_tilde_const_allowed = old;
}

fn with_banned_tilde_const(&mut self, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.is_tilde_const_allowed, false);
f(self);
self.is_tilde_const_allowed = old;
}

fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
let old = mem::replace(&mut self.is_let_allowed, allowed);
f(self, old);
Expand Down Expand Up @@ -130,19 +130,13 @@ impl<'a> AstValidator<'a> {
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.outer_impl_trait, outer);
if outer.is_some() {
self.with_bound_context(BoundContext::ImplTrait, |this| f(this));
self.with_banned_tilde_const(f);
} else {
f(self)
f(self);
}
self.outer_impl_trait = old;
}

fn with_bound_context(&mut self, ctx: BoundContext, f: impl FnOnce(&mut Self)) {
let old = self.bound_context.replace(ctx);
f(self);
self.bound_context = old;
}

fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
match constraint.kind {
AssocTyConstraintKind::Equality { .. } => {}
Expand All @@ -164,9 +158,7 @@ impl<'a> AstValidator<'a> {
TyKind::ImplTrait(..) => {
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
}
TyKind::TraitObject(..) => {
self.with_bound_context(BoundContext::TraitObject, |this| visit::walk_ty(this, t));
}
TyKind::TraitObject(..) => self.with_banned_tilde_const(|this| visit::walk_ty(this, t)),
TyKind::Path(ref qself, ref path) => {
// We allow these:
// - `Option<impl Trait>`
Expand Down Expand Up @@ -1083,13 +1075,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
unsafety,
polarity,
defaultness: _,
constness: _,
generics: _,
constness,
ref generics,
of_trait: Some(ref t),
ref self_ty,
items: _,
ref items,
}) => {
self.with_in_trait_impl(true, |this| {
self.with_in_trait_impl(true, Some(constness), |this| {
this.invalid_visibility(&item.vis, None);
if let TyKind::Err = self_ty.kind {
this.err_handler()
Expand All @@ -1112,7 +1104,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.emit();
}

visit::walk_item(this, item);
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);
}
this.visit_trait_ref(t);
this.visit_ty(self_ty);

walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl);
});
return; // Avoid visiting again.
}
Expand Down Expand Up @@ -1157,13 +1159,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.emit();
}
}
ItemKind::Fn(box FnKind(def, _, _, ref body)) => {
ItemKind::Fn(box FnKind(def, ref sig, ref generics, ref body)) => {
self.check_defaultness(item.span, def);

if body.is_none() {
let msg = "free function without a body";
self.error_item_without_body(item.span, "function", msg, " { <body> }");
}
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
if let Const::Yes(_) = sig.header.constness {
self.with_tilde_const_allowed(|this| this.visit_generics(generics));
} else {
self.visit_generics(generics);
}
let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref());
self.visit_fn(kind, item.span, item.id);
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
}
ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
let old_item = mem::replace(&mut self.extern_mod, Some(item));
Expand Down Expand Up @@ -1206,9 +1219,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
self.with_bound_context(BoundContext::TraitBounds, |this| {
walk_list!(this, visit_param_bound, bounds);
});
self.with_banned_tilde_const(|this| walk_list!(this, visit_param_bound, bounds));
walk_list!(self, visit_assoc_item, trait_items, AssocCtxt::Trait);
walk_list!(self, visit_attribute, &item.attrs);
return;
Expand Down Expand Up @@ -1281,7 +1292,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
_ => {}
}

visit::walk_item(self, item)
visit::walk_item(self, item);
}

fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
Expand Down Expand Up @@ -1428,15 +1439,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
match bound {
GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
if let Some(ctx) = self.bound_context {
let msg = format!("`?const` is not permitted in {}", ctx.description());
self.err_handler().span_err(bound.span(), &msg);
if !self.is_tilde_const_allowed {
self.err_handler()
.struct_span_err(bound.span(), "`~const` is not allowed here")
.note("only allowed on bounds on traits' associated types, const fns, const impls and its associated functions")
.emit();
}
}

GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
self.err_handler()
.span_err(bound.span(), "`?const` and `?` are mutually exclusive");
.span_err(bound.span(), "`~const` and `?` are mutually exclusive");
}

_ => {}
Expand Down Expand Up @@ -1589,7 +1602,32 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_item_named(item.ident, "const");
}

self.with_in_trait_impl(false, |this| visit::walk_assoc_item(this, item, ctxt));
match item.kind {
AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty))
if ctxt == AssocCtxt::Trait =>
{
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
walk_list!(self, visit_attribute, &item.attrs);
self.with_tilde_const_allowed(|this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds);
});
walk_list!(self, visit_ty, ty);
}
AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body))
if self.in_const_trait_impl =>
{
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.with_tilde_const_allowed(|this| this.visit_generics(generics));
let kind =
FnKind::Fn(FnCtxt::Assoc(ctxt), item.ident, sig, &item.vis, body.as_deref());
self.visit_fn(kind, item.span, item.id);
}
_ => self
.with_in_trait_impl(false, None, |this| visit::walk_assoc_item(this, item, ctxt)),
}
}
}

Expand Down Expand Up @@ -1683,9 +1721,10 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
session,
extern_mod: None,
in_trait_impl: false,
in_const_trait_impl: false,
has_proc_macro_decls: false,
outer_impl_trait: None,
bound_context: None,
is_tilde_const_allowed: false,
is_impl_trait_banned: false,
is_assoc_ty_bound_banned: false,
is_let_allowed: false,
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
gate_all!(generators, "yield syntax is experimental");
gate_all!(raw_ref_op, "raw address of syntax is experimental");
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
gate_all!(const_trait_impl, "const trait impls are experimental");
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
gate_all!(inline_const, "inline-const is experimental");
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,6 @@ declare_features! (
/// Allows `impl const Trait for T` syntax.
(active, const_trait_impl, "1.42.0", Some(67792), None),

/// Allows `T: ?const Trait` syntax in bounds.
(incomplete, const_trait_bound_opt_out, "1.42.0", Some(67794), None),

/// Allows the use of `no_sanitize` attribute.
(active, no_sanitize, "1.42.0", Some(39699), None),

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/removed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ declare_features! (
/// Allows overlapping impls of marker traits.
(removed, overlapping_marker_traits, "1.42.0", Some(29864), None,
Some("removed in favor of `#![feature(marker_trait_attr)]`")),
/// Allows `T: ?const Trait` syntax in bounds.
(removed, const_trait_bound_opt_out, "1.42.0", Some(67794), None,
Some("Removed in favor of `~const` bound in #![feature(const_trait_impl)]")),
/// Allows `#[no_debug]`.
(removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
/// Allows comparing raw pointers during const eval.
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::Constness;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use smallvec::SmallVec;
Expand Down Expand Up @@ -497,7 +496,7 @@ pub enum ImplSource<'tcx, N> {
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
Param(Vec<N>, Constness),
Param(Vec<N>, ty::BoundConstness),

/// Virtual calls through an object.
Object(ImplSourceObjectData<'tcx, N>),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<T> ExpectedFound<T> {
#[derive(Clone, Debug, TypeFoldable)]
pub enum TypeError<'tcx> {
Mismatch,
ConstnessMismatch(ExpectedFound<hir::Constness>),
ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
UnsafetyMismatch(ExpectedFound<hir::Unsafety>),
AbiMismatch(ExpectedFound<abi::Abi>),
Mutability,
Expand Down Expand Up @@ -102,7 +102,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
CyclicConst(_) => write!(f, "encountered a self-referencing constant"),
Mismatch => write!(f, "types differ"),
ConstnessMismatch(values) => {
write!(f, "expected {} fn, found {} fn", values.expected, values.found)
write!(f, "expected {} bound, found {} bound", values.expected, values.found)
}
UnsafetyMismatch(values) => {
write!(f, "expected {} fn, found {} fn", values.expected, values.found)
Expand Down
Loading

0 comments on commit ac50a53

Please sign in to comment.