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

Introduce ~const #88328

Merged
merged 10 commits into from
Aug 28, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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