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. - [ ] Add enum `BoundConstness` to the HIR. (With variants `NotConst` and `ConstIfConst` allowing future extensions) - [ ] Adjust trait selection and pre-existing code to use `BoundConstness`. - [ ] Optional steps (*for this PR, obviously*) - [ ] Fix #88155 - [ ] Do something with constness bounds in chalk
This commit is contained in:
parent
d5cd3205fd
commit
8660832086
16 changed files with 178 additions and 125 deletions
|
@ -284,7 +284,7 @@ impl ParenthesizedArgs {
|
||||||
|
|
||||||
pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
|
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.
|
/// Negative bounds should also be handled here.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
|
||||||
|
@ -295,10 +295,10 @@ pub enum TraitBoundModifier {
|
||||||
/// `?Trait`
|
/// `?Trait`
|
||||||
Maybe,
|
Maybe,
|
||||||
|
|
||||||
/// `?const Trait`
|
/// `~const Trait`
|
||||||
MaybeConst,
|
MaybeConst,
|
||||||
|
|
||||||
/// `?const ?Trait`
|
/// `~const ?Trait`
|
||||||
//
|
//
|
||||||
// This parses but will be rejected during AST validation.
|
// This parses but will be rejected during AST validation.
|
||||||
MaybeConstMaybe,
|
MaybeConstMaybe,
|
||||||
|
|
|
@ -1414,7 +1414,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
ref ty,
|
ref ty,
|
||||||
TraitBoundModifier::None | TraitBoundModifier::MaybeConst,
|
TraitBoundModifier::None | TraitBoundModifier::MaybeConst,
|
||||||
) => Some(this.lower_poly_trait_ref(ty, itctx.reborrow())),
|
) => 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.
|
// anyways, so treat it like `?Bound` as compilation proceeds.
|
||||||
GenericBound::Trait(
|
GenericBound::Trait(
|
||||||
_,
|
_,
|
||||||
|
|
|
@ -17,10 +17,11 @@ use rustc_errors::{error_code, pluralize, struct_span_err, Applicability};
|
||||||
use rustc_parse::validate_attr;
|
use rustc_parse::validate_attr;
|
||||||
use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY;
|
use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY;
|
||||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
|
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
|
||||||
use rustc_session::Session;
|
use rustc_session::{DiagnosticMessageId, Session};
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::{kw, sym, Ident};
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
@ -33,24 +34,6 @@ enum SelfSemantic {
|
||||||
No,
|
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> {
|
struct AstValidator<'a> {
|
||||||
session: &'a Session,
|
session: &'a Session,
|
||||||
|
|
||||||
|
@ -60,6 +43,8 @@ struct AstValidator<'a> {
|
||||||
/// Are we inside a trait impl?
|
/// Are we inside a trait impl?
|
||||||
in_trait_impl: bool,
|
in_trait_impl: bool,
|
||||||
|
|
||||||
|
in_const_trait_impl: bool,
|
||||||
|
|
||||||
has_proc_macro_decls: bool,
|
has_proc_macro_decls: bool,
|
||||||
|
|
||||||
/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
|
/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
|
||||||
|
@ -67,11 +52,7 @@ struct AstValidator<'a> {
|
||||||
/// e.g., `impl Iterator<Item = impl Debug>`.
|
/// e.g., `impl Iterator<Item = impl Debug>`.
|
||||||
outer_impl_trait: Option<Span>,
|
outer_impl_trait: Option<Span>,
|
||||||
|
|
||||||
/// Keeps track of the `BoundContext` as we recurse.
|
is_tilde_const_allowed: bool,
|
||||||
///
|
|
||||||
/// This is used to forbid `?const Trait` bounds in, e.g.,
|
|
||||||
/// `impl Iterator<Item = Box<dyn ?const Trait>`.
|
|
||||||
bound_context: Option<BoundContext>,
|
|
||||||
|
|
||||||
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
||||||
/// or `Foo::Bar<impl Trait>`
|
/// or `Foo::Bar<impl Trait>`
|
||||||
|
@ -88,10 +69,18 @@ struct AstValidator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 = 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);
|
f(self);
|
||||||
self.in_trait_impl = old;
|
self.in_trait_impl = old;
|
||||||
|
self.in_const_trait_impl = old_const;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
|
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
|
||||||
|
@ -100,6 +89,18 @@ impl<'a> AstValidator<'a> {
|
||||||
self.is_impl_trait_banned = old;
|
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)) {
|
fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
|
||||||
let old = mem::replace(&mut self.is_let_allowed, allowed);
|
let old = mem::replace(&mut self.is_let_allowed, allowed);
|
||||||
f(self, old);
|
f(self, old);
|
||||||
|
@ -130,19 +131,13 @@ impl<'a> AstValidator<'a> {
|
||||||
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
|
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
|
||||||
let old = mem::replace(&mut self.outer_impl_trait, outer);
|
let old = mem::replace(&mut self.outer_impl_trait, outer);
|
||||||
if outer.is_some() {
|
if outer.is_some() {
|
||||||
self.with_bound_context(BoundContext::ImplTrait, |this| f(this));
|
self.with_banned_tilde_const(f);
|
||||||
} else {
|
} else {
|
||||||
f(self)
|
f(self);
|
||||||
}
|
}
|
||||||
self.outer_impl_trait = old;
|
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) {
|
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
|
||||||
match constraint.kind {
|
match constraint.kind {
|
||||||
AssocTyConstraintKind::Equality { .. } => {}
|
AssocTyConstraintKind::Equality { .. } => {}
|
||||||
|
@ -164,9 +159,7 @@ impl<'a> AstValidator<'a> {
|
||||||
TyKind::ImplTrait(..) => {
|
TyKind::ImplTrait(..) => {
|
||||||
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
|
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
|
||||||
}
|
}
|
||||||
TyKind::TraitObject(..) => {
|
TyKind::TraitObject(..) => self.with_banned_tilde_const(|this| visit::walk_ty(this, t)),
|
||||||
self.with_bound_context(BoundContext::TraitObject, |this| visit::walk_ty(this, t));
|
|
||||||
}
|
|
||||||
TyKind::Path(ref qself, ref path) => {
|
TyKind::Path(ref qself, ref path) => {
|
||||||
// We allow these:
|
// We allow these:
|
||||||
// - `Option<impl Trait>`
|
// - `Option<impl Trait>`
|
||||||
|
@ -1083,13 +1076,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
unsafety,
|
unsafety,
|
||||||
polarity,
|
polarity,
|
||||||
defaultness: _,
|
defaultness: _,
|
||||||
constness: _,
|
constness,
|
||||||
generics: _,
|
ref generics,
|
||||||
of_trait: Some(ref t),
|
of_trait: Some(ref t),
|
||||||
ref self_ty,
|
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);
|
this.invalid_visibility(&item.vis, None);
|
||||||
if let TyKind::Err = self_ty.kind {
|
if let TyKind::Err = self_ty.kind {
|
||||||
this.err_handler()
|
this.err_handler()
|
||||||
|
@ -1112,7 +1105,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
.emit();
|
.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.
|
return; // Avoid visiting again.
|
||||||
}
|
}
|
||||||
|
@ -1157,13 +1160,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
.emit();
|
.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);
|
self.check_defaultness(item.span, def);
|
||||||
|
|
||||||
if body.is_none() {
|
if body.is_none() {
|
||||||
let msg = "free function without a body";
|
let msg = "free function without a body";
|
||||||
self.error_item_without_body(item.span, "function", msg, " { <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);
|
||||||
}
|
}
|
||||||
ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
|
ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
|
||||||
let old_item = mem::replace(&mut self.extern_mod, Some(item));
|
let old_item = mem::replace(&mut self.extern_mod, Some(item));
|
||||||
|
@ -1206,9 +1219,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
self.visit_vis(&item.vis);
|
self.visit_vis(&item.vis);
|
||||||
self.visit_ident(item.ident);
|
self.visit_ident(item.ident);
|
||||||
self.visit_generics(generics);
|
self.visit_generics(generics);
|
||||||
self.with_bound_context(BoundContext::TraitBounds, |this| {
|
self.with_banned_tilde_const(|this| walk_list!(this, visit_param_bound, bounds));
|
||||||
walk_list!(this, visit_param_bound, bounds);
|
|
||||||
});
|
|
||||||
walk_list!(self, visit_assoc_item, trait_items, AssocCtxt::Trait);
|
walk_list!(self, visit_assoc_item, trait_items, AssocCtxt::Trait);
|
||||||
walk_list!(self, visit_attribute, &item.attrs);
|
walk_list!(self, visit_attribute, &item.attrs);
|
||||||
return;
|
return;
|
||||||
|
@ -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) {
|
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
|
||||||
|
@ -1428,15 +1439,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
|
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
|
||||||
match bound {
|
match bound {
|
||||||
GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
|
GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
|
||||||
if let Some(ctx) = self.bound_context {
|
if !self.is_tilde_const_allowed {
|
||||||
let msg = format!("`?const` is not permitted in {}", ctx.description());
|
let msg = "`~const` is not allowed here";
|
||||||
self.err_handler().span_err(bound.span(), &msg);
|
let id_span_msg = (
|
||||||
|
DiagnosticMessageId::StabilityId(67792.try_into().ok()),
|
||||||
|
Some(bound.span()),
|
||||||
|
msg.to_owned(),
|
||||||
|
);
|
||||||
|
let fresh = self.session.one_time_diagnostics.borrow_mut().insert(id_span_msg);
|
||||||
|
if fresh {
|
||||||
|
self.err_handler()
|
||||||
|
.struct_span_err(bound.span(), msg)
|
||||||
|
.note("only allowed on bounds on traits' associated types, const fns, const impls and its associated functions")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
|
GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
|
||||||
self.err_handler()
|
self.err_handler()
|
||||||
.span_err(bound.span(), "`?const` and `?` are mutually exclusive");
|
.span_err(bound.span(), "`~const` and `?` are mutually exclusive");
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1589,7 +1611,32 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
self.check_item_named(item.ident, "const");
|
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)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,9 +1730,10 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
|
||||||
session,
|
session,
|
||||||
extern_mod: None,
|
extern_mod: None,
|
||||||
in_trait_impl: false,
|
in_trait_impl: false,
|
||||||
|
in_const_trait_impl: false,
|
||||||
has_proc_macro_decls: false,
|
has_proc_macro_decls: false,
|
||||||
outer_impl_trait: None,
|
outer_impl_trait: None,
|
||||||
bound_context: None,
|
is_tilde_const_allowed: false,
|
||||||
is_impl_trait_banned: false,
|
is_impl_trait_banned: false,
|
||||||
is_assoc_ty_bound_banned: false,
|
is_assoc_ty_bound_banned: false,
|
||||||
is_let_allowed: false,
|
is_let_allowed: false,
|
||||||
|
|
|
@ -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!(more_qualified_paths, "usage of qualified paths in this context is experimental");
|
||||||
gate_all!(generators, "yield syntax is experimental");
|
gate_all!(generators, "yield syntax is experimental");
|
||||||
gate_all!(raw_ref_op, "raw address of 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!(const_trait_impl, "const trait impls are experimental");
|
||||||
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
|
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
|
||||||
gate_all!(inline_const, "inline-const is experimental");
|
gate_all!(inline_const, "inline-const is experimental");
|
||||||
|
|
|
@ -515,9 +515,6 @@ declare_features! (
|
||||||
/// Allows `impl const Trait for T` syntax.
|
/// Allows `impl const Trait for T` syntax.
|
||||||
(active, const_trait_impl, "1.42.0", Some(67792), None),
|
(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.
|
/// Allows the use of `no_sanitize` attribute.
|
||||||
(active, no_sanitize, "1.42.0", Some(39699), None),
|
(active, no_sanitize, "1.42.0", Some(39699), None),
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,9 @@ declare_features! (
|
||||||
/// Allows overlapping impls of marker traits.
|
/// Allows overlapping impls of marker traits.
|
||||||
(removed, overlapping_marker_traits, "1.42.0", Some(29864), None,
|
(removed, overlapping_marker_traits, "1.42.0", Some(29864), None,
|
||||||
Some("removed in favor of `#![feature(marker_trait_attr)]`")),
|
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]`.
|
/// Allows `#[no_debug]`.
|
||||||
(removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
|
(removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
|
||||||
/// Allows comparing raw pointers during const eval.
|
/// Allows comparing raw pointers during const eval.
|
||||||
|
|
|
@ -457,10 +457,6 @@ pub enum PredicateKind<'tcx> {
|
||||||
/// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
|
/// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
|
||||||
/// the `Self` type of the trait reference and `A`, `B`, and `C`
|
/// the `Self` type of the trait reference and `A`, `B`, and `C`
|
||||||
/// would be the type parameters.
|
/// would be the type parameters.
|
||||||
///
|
|
||||||
/// A trait predicate will have `Constness::Const` if it originates
|
|
||||||
/// from a bound on a `const fn` without the `?const` opt-out (e.g.,
|
|
||||||
/// `const fn foobar<Foo: Bar>() {}`).
|
|
||||||
Trait(TraitPredicate<'tcx>),
|
Trait(TraitPredicate<'tcx>),
|
||||||
|
|
||||||
/// `where 'a: 'b`
|
/// `where 'a: 'b`
|
||||||
|
@ -633,8 +629,7 @@ pub struct TraitPredicate<'tcx> {
|
||||||
pub trait_ref: TraitRef<'tcx>,
|
pub trait_ref: TraitRef<'tcx>,
|
||||||
|
|
||||||
/// A trait predicate will have `Constness::Const` if it originates
|
/// A trait predicate will have `Constness::Const` if it originates
|
||||||
/// from a bound on a `const fn` without the `?const` opt-out (e.g.,
|
/// from a bound marked with `~const`.
|
||||||
/// `const fn foobar<Foo: Bar>() {}`).
|
|
||||||
pub constness: hir::Constness,
|
pub constness: hir::Constness,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_span::symbol::{kw, sym};
|
use rustc_span::symbol::{kw, sym};
|
||||||
|
|
||||||
/// Any `?` or `?const` modifiers that appear at the start of a bound.
|
/// Any `?` or `~const` modifiers that appear at the start of a bound.
|
||||||
struct BoundModifiers {
|
struct BoundModifiers {
|
||||||
/// `?Trait`.
|
/// `?Trait`.
|
||||||
maybe: Option<Span>,
|
maybe: Option<Span>,
|
||||||
|
|
||||||
/// `?const Trait`.
|
/// `~const Trait`.
|
||||||
maybe_const: Option<Span>,
|
maybe_const: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,6 +609,7 @@ impl<'a> Parser<'a> {
|
||||||
|| self.check_lifetime()
|
|| self.check_lifetime()
|
||||||
|| self.check(&token::Not) // Used for error reporting only.
|
|| self.check(&token::Not) // Used for error reporting only.
|
||||||
|| self.check(&token::Question)
|
|| self.check(&token::Question)
|
||||||
|
|| self.check(&token::Tilde)
|
||||||
|| self.check_keyword(kw::For)
|
|| self.check_keyword(kw::For)
|
||||||
|| self.check(&token::OpenDelim(token::Paren))
|
|| self.check(&token::OpenDelim(token::Paren))
|
||||||
}
|
}
|
||||||
|
@ -655,7 +656,7 @@ impl<'a> Parser<'a> {
|
||||||
let inner_lo = self.token.span;
|
let inner_lo = self.token.span;
|
||||||
let is_negative = self.eat(&token::Not);
|
let is_negative = self.eat(&token::Not);
|
||||||
|
|
||||||
let modifiers = self.parse_ty_bound_modifiers();
|
let modifiers = self.parse_ty_bound_modifiers()?;
|
||||||
let bound = if self.token.is_lifetime() {
|
let bound = if self.token.is_lifetime() {
|
||||||
self.error_lt_bound_with_modifiers(modifiers);
|
self.error_lt_bound_with_modifiers(modifiers);
|
||||||
self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
|
self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
|
||||||
|
@ -690,7 +691,7 @@ impl<'a> Parser<'a> {
|
||||||
if let Some(span) = modifiers.maybe_const {
|
if let Some(span) = modifiers.maybe_const {
|
||||||
self.struct_span_err(
|
self.struct_span_err(
|
||||||
span,
|
span,
|
||||||
"`?const` may only modify trait bounds, not lifetime bounds",
|
"`~const` may only modify trait bounds, not lifetime bounds",
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
|
@ -721,34 +722,27 @@ impl<'a> Parser<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `?const Trait`.
|
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `~const Trait`.
|
||||||
///
|
///
|
||||||
/// If no modifiers are present, this does not consume any tokens.
|
/// If no modifiers are present, this does not consume any tokens.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// TY_BOUND_MODIFIERS = "?" ["const" ["?"]]
|
/// TY_BOUND_MODIFIERS = ["~const"] ["?"]
|
||||||
/// ```
|
/// ```
|
||||||
fn parse_ty_bound_modifiers(&mut self) -> BoundModifiers {
|
fn parse_ty_bound_modifiers(&mut self) -> PResult<'a, BoundModifiers> {
|
||||||
if !self.eat(&token::Question) {
|
let maybe_const = if self.eat(&token::Tilde) {
|
||||||
return BoundModifiers { maybe: None, maybe_const: None };
|
let tilde = self.prev_token.span;
|
||||||
}
|
self.expect_keyword(kw::Const)?;
|
||||||
|
let span = tilde.to(self.prev_token.span);
|
||||||
|
self.sess.gated_spans.gate(sym::const_trait_impl, span);
|
||||||
|
Some(span)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// `? ...`
|
let maybe = if self.eat(&token::Question) { Some(self.prev_token.span) } else { None };
|
||||||
let first_question = self.prev_token.span;
|
|
||||||
if !self.eat_keyword(kw::Const) {
|
|
||||||
return BoundModifiers { maybe: Some(first_question), maybe_const: None };
|
|
||||||
}
|
|
||||||
|
|
||||||
// `?const ...`
|
Ok(BoundModifiers { maybe, maybe_const })
|
||||||
let maybe_const = first_question.to(self.prev_token.span);
|
|
||||||
self.sess.gated_spans.gate(sym::const_trait_bound_opt_out, maybe_const);
|
|
||||||
if !self.eat(&token::Question) {
|
|
||||||
return BoundModifiers { maybe: None, maybe_const: Some(maybe_const) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// `?const ? ...`
|
|
||||||
let second_question = self.prev_token.span;
|
|
||||||
BoundModifiers { maybe: Some(second_question), maybe_const: Some(maybe_const) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a type bound according to:
|
/// Parses a type bound according to:
|
||||||
|
@ -757,7 +751,7 @@ impl<'a> Parser<'a> {
|
||||||
/// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
|
/// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// For example, this grammar accepts `?const ?for<'a: 'b> m::Trait<'a>`.
|
/// For example, this grammar accepts `~const ?for<'a: 'b> m::Trait<'a>`.
|
||||||
fn parse_generic_ty_bound(
|
fn parse_generic_ty_bound(
|
||||||
&mut self,
|
&mut self,
|
||||||
lo: Span,
|
lo: Span,
|
||||||
|
|
|
@ -2227,8 +2227,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
||||||
match bound {
|
match bound {
|
||||||
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
|
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
|
||||||
let constness = match modifier {
|
let constness = match modifier {
|
||||||
hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst,
|
hir::TraitBoundModifier::None => hir::Constness::NotConst,
|
||||||
hir::TraitBoundModifier::None => constness,
|
hir::TraitBoundModifier::MaybeConst => constness,
|
||||||
// We ignore `where T: ?Sized`, it is already part of
|
// We ignore `where T: ?Sized`, it is already part of
|
||||||
// type parameter `T`.
|
// type parameter `T`.
|
||||||
hir::TraitBoundModifier::Maybe => continue,
|
hir::TraitBoundModifier::Maybe => continue,
|
||||||
|
@ -2497,8 +2497,8 @@ fn predicates_from_bound<'tcx>(
|
||||||
hir::GenericBound::Trait(ref tr, modifier) => {
|
hir::GenericBound::Trait(ref tr, modifier) => {
|
||||||
let constness = match modifier {
|
let constness = match modifier {
|
||||||
hir::TraitBoundModifier::Maybe => return vec![],
|
hir::TraitBoundModifier::Maybe => return vec![],
|
||||||
hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst,
|
hir::TraitBoundModifier::MaybeConst => constness,
|
||||||
hir::TraitBoundModifier::None => constness,
|
hir::TraitBoundModifier::None => hir::Constness::NotConst,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bounds = Bounds::default();
|
let mut bounds = Bounds::default();
|
||||||
|
|
|
@ -387,7 +387,7 @@ impl clean::GenericBound {
|
||||||
let modifier_str = match modifier {
|
let modifier_str = match modifier {
|
||||||
hir::TraitBoundModifier::None => "",
|
hir::TraitBoundModifier::None => "",
|
||||||
hir::TraitBoundModifier::Maybe => "?",
|
hir::TraitBoundModifier::Maybe => "?",
|
||||||
hir::TraitBoundModifier::MaybeConst => "?const",
|
hir::TraitBoundModifier::MaybeConst => "~const",
|
||||||
};
|
};
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
write!(f, "{}{:#}", modifier_str, ty.print(cx))
|
write!(f, "{}{:#}", modifier_str, ty.print(cx))
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
// issue-49296: Unsafe shenigans in constants can result in missing errors
|
// issue-49296: Unsafe shenigans in constants can result in missing errors
|
||||||
|
|
||||||
#![feature(const_fn_trait_bound)]
|
#![feature(const_fn_trait_bound)]
|
||||||
#![feature(const_trait_bound_opt_out)]
|
|
||||||
#![allow(incomplete_features)]
|
|
||||||
|
|
||||||
const unsafe fn transmute<T: ?const Copy, U: ?const Copy>(t: T) -> U {
|
use std::mem::transmute;
|
||||||
#[repr(C)]
|
|
||||||
union Transmute<T: Copy, U: Copy> {
|
|
||||||
from: T,
|
|
||||||
to: U,
|
|
||||||
}
|
|
||||||
|
|
||||||
Transmute { from: t }.to
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn wat(x: u64) -> &'static u64 {
|
const fn wat(x: u64) -> &'static u64 {
|
||||||
unsafe { transmute(&x) }
|
unsafe { transmute(&x) }
|
||||||
}
|
}
|
||||||
|
|
||||||
const X: u64 = *wat(42);
|
const X: u64 = *wat(42);
|
||||||
//~^ ERROR evaluation of constant value failed
|
//~^ ERROR evaluation of constant value failed
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,10 @@ impl PartialEq for S {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn equals_self<T: ?const PartialEq>(t: &T) -> bool {
|
const fn equals_self<T: PartialEq>(t: &T) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const EQ: bool = equals_self(&S);
|
pub const EQ: bool = equals_self(&S);
|
||||||
|
|
||||||
// Calling `equals_self` with a type that only has a non-const impl is fine, because we opted out.
|
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
fn a<T: ~const From<u8>>() {}
|
||||||
|
//~^ ERROR: `~const` is not allowed
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn b<T: ~const From<u8>>() {}
|
||||||
|
//~^ ERROR: `~const` is not allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
error: `~const` is not allowed here
|
||||||
|
--> $DIR/tilde-const-on-non-const-fn.rs:2:9
|
||||||
|
|
|
||||||
|
LL | fn a<T: ~const From<u8>>() {}
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: only allowed on bounds on traits' associated types, const fns, const impls and its associated functions
|
||||||
|
|
||||||
|
error: `~const` is not allowed here
|
||||||
|
--> $DIR/tilde-const-on-non-const-fn.rs:8:13
|
||||||
|
|
|
||||||
|
LL | fn b<T: ~const From<u8>>() {}
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: only allowed on bounds on traits' associated types, const fns, const impls and its associated functions
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
|
@ -537,10 +537,10 @@ impl Rewrite for ast::GenericBound {
|
||||||
.map(|s| format!("?{}", s)),
|
.map(|s| format!("?{}", s)),
|
||||||
ast::TraitBoundModifier::MaybeConst => poly_trait_ref
|
ast::TraitBoundModifier::MaybeConst => poly_trait_ref
|
||||||
.rewrite(context, shape.offset_left(7)?)
|
.rewrite(context, shape.offset_left(7)?)
|
||||||
.map(|s| format!("?const {}", s)),
|
.map(|s| format!("~const {}", s)),
|
||||||
ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref
|
ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref
|
||||||
.rewrite(context, shape.offset_left(8)?)
|
.rewrite(context, shape.offset_left(8)?)
|
||||||
.map(|s| format!("?const ?{}", s)),
|
.map(|s| format!("~const ?{}", s)),
|
||||||
};
|
};
|
||||||
rewrite.map(|s| if has_paren { format!("({})", s) } else { s })
|
rewrite.map(|s| if has_paren { format!("({})", s) } else { s })
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,35 +145,33 @@ type MyFn = fn(
|
||||||
b: SomeOtherLongComplexType,
|
b: SomeOtherLongComplexType,
|
||||||
) -> Box<Future<Item = AnotherLongType, Error = ALongErrorType>>;
|
) -> Box<Future<Item = AnotherLongType, Error = ALongErrorType>>;
|
||||||
|
|
||||||
// Const opt-out
|
// Const bound
|
||||||
|
|
||||||
trait T: ?const Super {}
|
trait T: ~const Super {}
|
||||||
|
|
||||||
const fn maybe_const<S: ?const T>() -> i32 {
|
const fn not_quite_const<S: ~const T>() -> i32 {
|
||||||
<S as T>::CONST
|
<S as T>::CONST
|
||||||
}
|
}
|
||||||
|
|
||||||
struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
|
struct S<T: ~const ?Sized>(std::marker::PhantomData<T>);
|
||||||
|
|
||||||
impl ?const T {}
|
impl ~const T {}
|
||||||
|
|
||||||
fn trait_object() -> &'static dyn ?const T {
|
fn trait_object() -> &'static dyn ~const T {
|
||||||
&S
|
&S
|
||||||
}
|
}
|
||||||
|
|
||||||
fn i(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
|
fn i(_: impl IntoIterator<Item = Box<dyn ~const T>>) {}
|
||||||
|
|
||||||
fn apit(_: impl ?const T) {}
|
fn apit(_: impl ~const T) {}
|
||||||
|
|
||||||
fn rpit() -> impl ?const T {
|
fn rpit() -> impl ~const T {
|
||||||
S
|
S
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Foo<T: Trait>(T);
|
pub struct Foo<T: Trait>(T);
|
||||||
impl<T: ?const Trait> Foo<T> {
|
impl<T: ~const Trait> Foo<T> {
|
||||||
fn new(t: T) -> Self {
|
fn new(t: T) -> Self {
|
||||||
// not calling methods on `t`, so we opt out of requiring
|
|
||||||
// `<T as Trait>` to have const methods via `?const`
|
|
||||||
Self(t)
|
Self(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue