Rollup merge of #154527 - fmease:more-soft-gates, r=nnethercote

Emit pre-expansion feature gate warnings for negative impls and specialization

Follow up to rust-lang/rust#154475; part of rust-lang/rust#154045.

This shouldn't need any extra input from T-compiler or T-lang since it's legitimized by [MCP 535](https://github.com/rust-lang/compiler-team/issues/535).

However, I have a feeling that negative impls & specialization behind "`#[cfg(feature = "nightly")]`" are more prevalent in the ecosystem compared to e.g., auto traits & box patterns, so these new warnings will probably hit a bunch of users. In any case, it's only a warning for now not an error so e.g., running crater "in deny mode" or nominating for a T-lang meeting discussion would be disproportionate (esp. since T-lang [has confirmed in the past](https://github.com/rust-lang/rust/pull/116393#issuecomment-1745571299) that this is a T-compiler matter).
This commit is contained in:
Jonathan Brouwer
2026-04-02 22:13:50 +02:00
committed by GitHub
27 changed files with 470 additions and 295 deletions
+177 -190
View File
@@ -14,31 +14,22 @@
/// The common case.
macro_rules! gate {
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
($visitor:expr, $feature:ident, $span:expr, $explain:expr $(, $help:expr)?) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit();
}
}};
($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit();
feature_err($visitor.sess, sym::$feature, $span, $explain)
$(.with_help($help))?
.emit();
}
}};
}
/// The unusual case, where the `has_feature` condition is non-standard.
macro_rules! gate_alt {
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr $(, $notes:expr)?) => {{
if !$has_feature && !$span.allows_unstable($name) {
feature_err(&$visitor.sess, $name, $span, $explain).emit();
}
}};
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{
if !$has_feature && !$span.allows_unstable($name) {
let mut diag = feature_err(&$visitor.sess, $name, $span, $explain);
for note in $notes {
diag.note(*note);
}
#[allow(unused_mut)]
let mut diag = feature_err($visitor.sess, $name, $span, $explain);
$(for &note in $notes { diag.note(note); })?
diag.emit();
}
}};
@@ -51,21 +42,12 @@ macro_rules! gate_multi {
let spans: Vec<_> =
$spans.filter(|span| !span.allows_unstable(sym::$feature)).collect();
if !spans.is_empty() {
feature_err(&$visitor.sess, sym::$feature, spans, $explain).emit();
feature_err($visitor.sess, sym::$feature, spans, $explain).emit();
}
}
}};
}
/// The legacy case.
macro_rules! gate_legacy {
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
feature_warn(&$visitor.sess, sym::$feature, $span, $explain);
}
}};
}
pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
PostExpansionVisitor { sess, features }.visit_attribute(attr)
}
@@ -77,6 +59,15 @@ struct PostExpansionVisitor<'a> {
features: &'a Features,
}
// -----------------------------------------------------------------------------
// POST-EXPANSION FEATURE GATES FOR UNSTABLE ATTRIBUTES ETC.
// **LEGACY** POST-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX **LEGACY**
// -----------------------------------------------------------------------------
// IMPORTANT: Don't add any new post-expansion feature gates for new unstable syntax!
// It's a legacy mechanism for them.
// Instead, register a pre-expansion feature gate using `gate_all` in fn `check_crate`.
impl<'a> PostExpansionVisitor<'a> {
/// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) {
@@ -89,14 +80,14 @@ fn visit_ty(&mut self, ty: &ast::Ty) {
if let ast::TyKind::ImplTrait(..) = ty.kind {
if self.in_associated_ty {
gate!(
&self.vis,
self.vis,
impl_trait_in_assoc_type,
ty.span,
"`impl Trait` in associated types is unstable"
);
} else {
gate!(
&self.vis,
self.vis,
type_alias_impl_trait,
ty.span,
"`impl Trait` in type aliases is unstable"
@@ -211,7 +202,7 @@ fn visit_item(&mut self, i: &'a ast::Item) {
for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
if item.has_name(sym::simd) {
gate!(
&self,
self,
repr_simd,
attr.span,
"SIMD types are experimental and possibly buggy"
@@ -224,35 +215,30 @@ fn visit_item(&mut self, i: &'a ast::Item) {
ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) => {
if let ast::ImplPolarity::Negative(span) = of_trait.polarity {
gate!(
&self,
self,
negative_impls,
span.to(of_trait.trait_ref.path.span),
"negative trait bounds are not fully implemented; \
use marker types for now"
"negative impls are experimental",
"use marker types for now"
);
}
if let ast::Defaultness::Default(_) = of_trait.defaultness {
gate!(&self, specialization, i.span, "specialization is unstable");
gate!(self, specialization, i.span, "specialization is experimental");
}
}
ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => {
gate!(
&self,
auto_traits,
i.span,
"auto traits are experimental and possibly buggy"
);
gate!(self, auto_traits, i.span, "auto traits are experimental and possibly buggy");
}
ast::ItemKind::TraitAlias(..) => {
gate!(&self, trait_alias, i.span, "trait aliases are experimental");
gate!(self, trait_alias, i.span, "trait aliases are experimental");
}
ast::ItemKind::MacroDef(_, ast::MacroDef { macro_rules: false, .. }) => {
let msg = "`macro` is experimental";
gate!(&self, decl_macro, i.span, msg);
gate!(self, decl_macro, i.span, msg);
}
ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
@@ -264,7 +250,7 @@ fn visit_item(&mut self, i: &'a ast::Item) {
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable");
gate!(self, min_generic_const_args, i.span, "top-level `type const` are unstable");
}
_ => {}
@@ -280,7 +266,7 @@ fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
let links_to_llvm = link_name.is_some_and(|val| val.as_str().starts_with("llvm."));
if links_to_llvm {
gate!(
&self,
self,
link_llvm_intrinsics,
i.span,
"linking to LLVM intrinsics is experimental"
@@ -288,7 +274,7 @@ fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
}
}
ast::ForeignItemKind::TyAlias(..) => {
gate!(&self, extern_types, i.span, "extern types are experimental");
gate!(self, extern_types, i.span, "extern types are experimental");
}
ast::ForeignItemKind::MacCall(..) => {}
}
@@ -303,10 +289,10 @@ fn visit_ty(&mut self, ty: &'a ast::Ty) {
self.check_late_bound_lifetime_defs(&fn_ptr_ty.generic_params);
}
ast::TyKind::Never => {
gate!(&self, never_type, ty.span, "the `!` type is experimental");
gate!(self, never_type, ty.span, "the `!` type is experimental");
}
ast::TyKind::Pat(..) => {
gate!(&self, pattern_types, ty.span, "pattern types are unstable");
gate!(self, pattern_types, ty.span, "pattern types are unstable");
}
_ => {}
}
@@ -339,7 +325,7 @@ fn visit_generic_args(&mut self, args: &'a ast::GenericArgs) {
&& let ast::FnRetTy::Ty(ref ty) = generic_args.output
&& matches!(ty.kind, ast::TyKind::Never)
{
gate!(&self, never_type, ty.span, "the `!` type is experimental");
gate!(self, never_type, ty.span, "the `!` type is experimental");
}
visit::walk_generic_args(self, args);
}
@@ -348,7 +334,7 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
ast::ExprKind::TryBlock(_, None) => {
// `try { ... }` is old and is only gated post-expansion here.
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
gate!(self, try_blocks, e.span, "`try` expression is experimental");
}
ast::ExprKind::TryBlock(_, Some(_)) => {
// `try_blocks_heterogeneous` is new, and gated pre-expansion instead.
@@ -359,10 +345,10 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
..
}) => match suffix {
Some(sym::f16) => {
gate!(&self, f16, e.span, "the type `f16` is unstable")
gate!(self, f16, e.span, "the type `f16` is unstable")
}
Some(sym::f128) => {
gate!(&self, f128, e.span, "the type `f128` is unstable")
gate!(self, f128, e.span, "the type `f128` is unstable")
}
_ => (),
},
@@ -381,7 +367,7 @@ fn visit_pat(&mut self, pattern: &'a ast::Pat) {
};
if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind {
gate!(
&self,
self,
half_open_range_patterns_in_slices,
pat.span,
"`X..` patterns in slices are experimental"
@@ -390,7 +376,7 @@ fn visit_pat(&mut self, pattern: &'a ast::Pat) {
}
}
PatKind::Box(..) => {
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
gate!(self, box_patterns, pattern.span, "box pattern syntax is experimental");
}
_ => {}
}
@@ -412,7 +398,7 @@ fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId)
}
if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
gate!(&self, c_variadic, span, "C-variadic functions are unstable");
gate!(self, c_variadic, span, "C-variadic functions are unstable");
}
visit::walk_fn(self, fn_kind)
@@ -424,7 +410,7 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
ast::AssocItemKind::Type(box ast::TyAlias { ty, .. }) => {
if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
gate!(
&self,
self,
associated_type_defaults,
i.span,
"associated type defaults are unstable"
@@ -441,18 +427,13 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(
&self,
min_generic_const_args,
i.span,
"associated `type const` are unstable"
);
gate!(self, min_generic_const_args, i.span, "associated `type const` are unstable");
// Make sure associated `type const` defaults in traits are only allowed
// if the feature gate is enabled.
// #![feature(associated_type_defaults)]
if ctxt == AssocCtxt::Trait && rhs.is_some() {
gate!(
&self,
self,
associated_type_defaults,
i.span,
"associated type defaults are unstable"
@@ -469,13 +450,15 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
self.features.specialization() || (is_fn && self.features.min_specialization()),
sym::specialization,
i.span,
"specialization is unstable"
"specialization is experimental"
);
}
visit::walk_assoc_item(self, i, ctxt)
}
}
// -----------------------------------------------------------------------------
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
maybe_stage_features(sess, features, krate);
check_incompatible_features(sess, features);
@@ -484,166 +467,170 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
let mut visitor = PostExpansionVisitor { sess, features };
// -----------------------------------------------------------------------------
// PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX
// -----------------------------------------------------------------------------
let spans = sess.psess.gated_spans.spans.borrow();
macro_rules! gate_all {
($gate:ident, $msg:literal) => {
if let Some(spans) = spans.get(&sym::$gate) {
for span in spans {
gate!(&visitor, $gate, *span, $msg);
}
}
};
($gate:ident, $msg:literal, $help:literal) => {
if let Some(spans) = spans.get(&sym::$gate) {
for span in spans {
gate!(&visitor, $gate, *span, $msg, $help);
}
($feature:ident, $explain:literal $(, $help:literal)?) => {
for &span in spans.get(&sym::$feature).into_iter().flatten() {
gate!(visitor, $feature, span, $explain $(, $help)?);
}
};
}
// tidy-alphabetical-start
gate_all!(async_for_loop, "`for await` loops are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(const_block_items, "const block items are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(const_trait_impl, "const trait impls are experimental");
gate_all!(contracts, "contracts are incomplete");
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(default_field_values, "default values on fields are experimental");
gate_all!(ergonomic_clones, "ergonomic clones are experimental");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(gen_blocks, "gen blocks are experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(global_registration, "global registration is experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
gate_all!(impl_restriction, "`impl` restrictions are experimental");
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(super_let, "`super let` is experimental");
gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental");
gate_all!(unsafe_binders, "unsafe binder types are experimental");
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(yeet_expr, "`do yeet` expression is experimental");
// tidy-alphabetical-end
gate_all!(
async_trait_bounds,
"`async` trait bounds are unstable",
"use the desugared name of the async trait, such as `AsyncFn`"
);
gate_all!(async_for_loop, "`for await` loops are experimental");
gate_all!(
closure_lifetime_binder,
"`for<...>` binders for closures are experimental",
"consider removing `for<...>`"
);
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
// yield can be enabled either by `coroutines` or `gen_blocks`
if let Some(spans) = spans.get(&sym::yield_expr) {
for span in spans {
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
// Emit yield_expr as the error, since that will be sufficient. You can think of it
// as coroutines and gen_blocks imply yield_expr.
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
.emit();
}
}
}
gate_all!(gen_blocks, "gen blocks are experimental");
gate_all!(const_trait_impl, "const trait impls are experimental");
gate_all!(
half_open_range_patterns_in_slices,
"half-open range patterns in slices are unstable"
);
gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental");
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(ergonomic_clones, "ergonomic clones are experimental");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
gate_all!(default_field_values, "default values on fields are experimental");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
// associated_const_equality is stabilized as part of min_generic_const_args
if let Some(spans) = spans.get(&sym::associated_const_equality) {
for span in spans {
if !visitor.features.min_generic_const_args()
&& !span.allows_unstable(sym::min_generic_const_args)
{
feature_err(
&visitor.sess,
sym::min_generic_const_args,
*span,
"associated const equality is incomplete",
)
.emit();
}
}
}
// `mgca_type_const_syntax` is part of `min_generic_const_args` so either
// or both are enabled we don't need to emit a feature error.
if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) {
for span in spans {
if visitor.features.min_generic_const_args()
|| visitor.features.mgca_type_const_syntax()
|| span.allows_unstable(sym::min_generic_const_args)
|| span.allows_unstable(sym::mgca_type_const_syntax)
{
continue;
}
feature_err(
&visitor.sess,
sym::min_generic_const_args,
*span,
"`type const` syntax is experimental",
)
.emit();
}
// `associated_const_equality` will be stabilized as part of `min_generic_const_args`.
for &span in spans.get(&sym::associated_const_equality).into_iter().flatten() {
gate!(visitor, min_generic_const_args, span, "associated const equality is incomplete");
}
gate_all!(global_registration, "global registration is experimental");
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
gate_all!(unsafe_binders, "unsafe binder types are experimental");
gate_all!(contracts, "contracts are incomplete");
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(impl_restriction, "`impl` restrictions are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
for &span in spans {
if span.allows_unstable(sym::never_patterns) {
continue;
}
let sm = sess.source_map();
// We gate two types of spans: the span of a `!` pattern, and the span of a
// match arm without a body. For the latter we want to give the user a normal
// error.
if let Ok(snippet) = sm.span_to_snippet(span)
&& snippet == "!"
{
feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental")
.emit();
} else {
let suggestion = span.shrink_to_hi();
sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion });
}
}
// `mgca_type_const_syntax` is part of `min_generic_const_args` so if
// either or both are enabled we don't need to emit a feature error.
for &span in spans.get(&sym::mgca_type_const_syntax).into_iter().flatten() {
if visitor.features.min_generic_const_args()
|| visitor.features.mgca_type_const_syntax()
|| span.allows_unstable(sym::min_generic_const_args)
|| span.allows_unstable(sym::mgca_type_const_syntax)
{
continue;
}
feature_err(
visitor.sess,
sym::min_generic_const_args,
span,
"`type const` syntax is experimental",
)
.emit();
}
// Negative bounds are *super* internal.
// Under no circumstances do we want to advertise the feature name to users!
if !visitor.features.negative_bounds() {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
for &span in spans.get(&sym::negative_bounds).into_iter().flatten() {
sess.dcx().emit_err(errors::NegativeBoundUnsupported { span });
}
}
// All uses of `gate_all_legacy_dont_use!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).
// We emit an early future-incompatible warning for these.
// New syntax gates should go above here to get a hard error gate.
macro_rules! gate_all_legacy_dont_use {
($gate:ident, $msg:literal) => {
for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
gate_legacy!(&visitor, $gate, *span, $msg);
if !visitor.features.never_patterns() {
for &span in spans.get(&sym::never_patterns).into_iter().flatten() {
if span.allows_unstable(sym::never_patterns) {
continue;
}
// We gate two types of spans: the span of a `!` pattern, and the span of a
// match arm without a body. For the latter we want to give the user a normal
// error.
if let Ok("!") = sess.source_map().span_to_snippet(span).as_deref() {
feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental")
.emit();
} else {
let suggestion = span.shrink_to_hi();
sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion });
}
}
}
// Yield exprs can be enabled either by `yield_expr`, by `coroutines` or by `gen_blocks`.
for &span in spans.get(&sym::yield_expr).into_iter().flatten() {
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
// Only mentioned `yield_expr` in the diagnostic since that'll be sufficient.
// You can think of it as `coroutines` and `gen_blocks` implying `yield_expr`.
feature_err(visitor.sess, sym::yield_expr, span, "yield syntax is experimental").emit();
}
}
// -----------------------------------------------------------------------------
// **LEGACY** SOFT PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX **LEGACY**
// -----------------------------------------------------------------------------
// IMPORTANT: Do not extend the list below! New syntax should go above and use `gate_all`.
// FIXME(#154045): Migrate all of these to erroring feature gates and
// remove the corresponding post-expansion feature gates.
macro_rules! soft_gate_all_legacy_dont_use {
($feature:ident, $explain:literal) => {
for &span in spans.get(&sym::$feature).into_iter().flatten() {
if !visitor.features.$feature() && !span.allows_unstable(sym::$feature) {
feature_warn(&visitor.sess, sym::$feature, span, $explain);
}
}
};
}
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental");
gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable");
gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable");
// tidy-alphabetical-start
soft_gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable");
soft_gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
soft_gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental");
soft_gate_all_legacy_dont_use!(negative_impls, "negative impls are experimental");
soft_gate_all_legacy_dont_use!(specialization, "specialization is experimental");
soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable");
// tidy-alphabetical-end
for &span in spans.get(&sym::min_specialization).into_iter().flatten() {
if !visitor.features.specialization()
&& !visitor.features.min_specialization()
&& !span.allows_unstable(sym::specialization)
&& !span.allows_unstable(sym::min_specialization)
{
feature_warn(visitor.sess, sym::specialization, span, "specialization is experimental");
}
}
// -----------------------------------------------------------------------------
visit::walk_crate(&mut visitor, krate);
}
+11 -1
View File
@@ -248,10 +248,18 @@ fn parse_item_kind(
self.parse_use_item()?
} else if self.check_fn_front_matter(check_pub, case) {
// FUNCTION ITEM
let defaultness = def_();
if let Defaultness::Default(span) = defaultness {
// Default functions should only require feature `min_specialization`. We remove the
// `specialization` tag again as such spans *require* feature `specialization` to be
// enabled. In a later stage, we make `specialization` imply `min_specialization`.
self.psess.gated_spans.gate(sym::min_specialization, span);
self.psess.gated_spans.ungate_last(sym::specialization, span);
}
let (ident, sig, generics, contract, body) =
self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
ItemKind::Fn(Box::new(Fn {
defaultness: def_(),
defaultness,
ident,
sig,
generics,
@@ -603,6 +611,7 @@ fn is_async_fn(&self) -> bool {
fn parse_polarity(&mut self) -> ast::ImplPolarity {
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
if self.check(exp!(Bang)) && self.look_ahead(1, |t| t.can_begin_type()) {
self.psess.gated_spans.gate(sym::negative_impls, self.token.span);
self.bump(); // `!`
ast::ImplPolarity::Negative(self.prev_token.span)
} else {
@@ -1015,6 +1024,7 @@ fn parse_defaultness(&mut self) -> Defaultness {
if self.check_keyword(exp!(Default))
&& self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
{
self.psess.gated_spans.gate(sym::specialization, self.token.span);
self.bump(); // `default`
Defaultness::Default(self.prev_token_uninterpolated_span())
} else if self.eat_keyword(exp!(Final)) {
-7
View File
@@ -1,7 +0,0 @@
auto trait MyTrait {}
//~^ ERROR auto traits are experimental and possibly buggy
impl<T> !MyTrait for *mut T {}
//~^ ERROR negative trait bounds are not fully implemented
fn main() {}
-23
View File
@@ -1,23 +0,0 @@
error[E0658]: auto traits are experimental and possibly buggy
--> $DIR/ungated-impl.rs:1:1
|
LL | auto trait MyTrait {}
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #13231 <https://github.com/rust-lang/rust/issues/13231> for more information
= help: add `#![feature(auto_traits)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: negative trait bounds are not fully implemented; use marker types for now
--> $DIR/ungated-impl.rs:4:9
|
LL | impl<T> !MyTrait for *mut T {}
| ^^^^^^^^
|
= note: see issue #68318 <https://github.com/rust-lang/rust/issues/68318> for more information
= help: add `#![feature(negative_impls)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.
@@ -1,12 +1,5 @@
// Test that default and negative trait implementations are gated by
// `auto_traits` feature gate
auto trait DummyAutoTrait {} //~ ERROR auto traits are experimental and possibly buggy
struct DummyStruct;
auto trait AutoDummyTrait {}
//~^ ERROR auto traits are experimental and possibly buggy
impl !AutoDummyTrait for DummyStruct {}
//~^ ERROR negative trait bounds are not fully implemented; use marker types for now
pub unsafe auto trait AnotherAutoTrait {} //~ ERROR auto traits are experimental and possibly buggy
fn main() {}
@@ -1,21 +1,21 @@
error[E0658]: auto traits are experimental and possibly buggy
--> $DIR/feature-gate-auto-traits.rs:6:1
--> $DIR/feature-gate-auto-traits.rs:1:1
|
LL | auto trait AutoDummyTrait {}
LL | auto trait DummyAutoTrait {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #13231 <https://github.com/rust-lang/rust/issues/13231> for more information
= help: add `#![feature(auto_traits)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: negative trait bounds are not fully implemented; use marker types for now
--> $DIR/feature-gate-auto-traits.rs:9:6
error[E0658]: auto traits are experimental and possibly buggy
--> $DIR/feature-gate-auto-traits.rs:3:1
|
LL | impl !AutoDummyTrait for DummyStruct {}
| ^^^^^^^^^^^^^^^
LL | pub unsafe auto trait AnotherAutoTrait {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #68318 <https://github.com/rust-lang/rust/issues/68318> for more information
= help: add `#![feature(negative_impls)]` to the crate attributes to enable
= note: see issue #13231 <https://github.com/rust-lang/rust/issues/13231> for more information
= help: add `#![feature(auto_traits)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 2 previous errors
@@ -1,6 +1,6 @@
// For historical reasons, auto traits don't have a proper pre-expansion feature gate.
// We're now at least issuing a *warning* for those that only exist before macro expansion.
// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one.
// For historical reasons, auto traits don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-auto-traits.rs`.
//@ check-pass
@@ -1,6 +1,6 @@
// For historical reasons, box patterns don't have a proper pre-expansion feature gate.
// We're now at least issuing a *warning* for those that only exist before macro expansion.
// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one.
// For historical reasons, box patterns don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-box_patterns.rs`.
//@ check-pass
@@ -1,6 +1,6 @@
// For historical reasons, decl macros 2.0 don't have a proper pre-expansion feature gate.
// We're now at least issuing a *warning* for those that only exist before macro expansion.
// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one.
// For historical reasons, decl macros 2.0 don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-decl_macro.rs`.
//@ check-pass
@@ -0,0 +1,12 @@
// For historical reasons, negative impls don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-negative_impls.rs`.
//@ check-pass
#[cfg(false)]
impl !Trait for () {}
//~^ WARN negative impls are experimental
//~| WARN unstable syntax can change at any point in the future
fn main() {}
@@ -0,0 +1,14 @@
warning: negative impls are experimental
--> $DIR/soft-feature-gate-negative_impls.rs:8:6
|
LL | impl !Trait for () {}
| ^
|
= note: see issue #68318 <https://github.com/rust-lang/rust/issues/68318> for more information
= help: add `#![feature(negative_impls)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: 1 warning emitted
@@ -1,6 +1,6 @@
// For historical reasons, trait aliases don't have a proper pre-expansion feature gate.
// We're now at least issuing a *warning* for those that only exist before macro expansion.
// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one.
// For historical reasons, trait aliases don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-trait-alias.rs`.
//@ check-pass
@@ -1,6 +1,6 @@
// For historical reasons, try blocks don't have a proper pre-expansion feature gate.
// We're now at least issuing a *warning* for those that only exist before macro expansion.
// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one.
// For historical reasons, try blocks don't have an erroring pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-try_blocks.rs`.
//@ edition: 2018
//@ check-pass
@@ -46,6 +46,16 @@ error[E0405]: cannot find trait `r#use` in this scope
LL | fn binder() -> impl Sized + for<'a> use<> {}
| ^^^ not found in this scope
error[E0658]: const trait impls are experimental
--> $DIR/bound-modifiers.rs:12:32
|
LL | fn constness() -> impl Sized + const use<> {}
| ^^^^^
|
= note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `async` trait bounds are unstable
--> $DIR/bound-modifiers.rs:7:32
|
@@ -57,16 +67,6 @@ LL | fn asyncness() -> impl Sized + async use<> {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= help: use the desugared name of the async trait, such as `AsyncFn`
error[E0658]: const trait impls are experimental
--> $DIR/bound-modifiers.rs:12:32
|
LL | fn constness() -> impl Sized + const use<> {}
| ^^^^^
|
= note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 10 previous errors
Some errors have detailed explanations: E0405, E0658.
+2 -1
View File
@@ -9,12 +9,13 @@
#![feature(const_trait_impl)]
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(macro_guard_matcher)]
#![feature(more_qualified_paths)]
#![feature(never_patterns)]
#![feature(specialization)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(yeet_expr)]
#![feature(macro_guard_matcher)]
#![deny(unused_macros)]
// These macros force the use of AST pretty-printing by converting the input to
@@ -1,4 +1,5 @@
//@ check-pass
#![feature(specialization)]
fn main() {}
@@ -0,0 +1,21 @@
trait Trait {
type Ty;
const CT: ();
fn fn_(&self);
}
impl<T> Trait for T {
default type Ty = (); //~ ERROR specialization is experimental
default const CT: () = (); //~ ERROR specialization is experimental
default fn fn_(&self) {} //~ ERROR specialization is experimental
}
trait OtherTrait {
fn fn_();
}
default impl<T> OtherTrait for T { //~ ERROR specialization is experimental
fn fn_() {}
}
fn main() {}
@@ -0,0 +1,45 @@
error[E0658]: specialization is experimental
--> $DIR/feature-gate-specialization.rs:8:5
|
LL | default type Ty = ();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: specialization is experimental
--> $DIR/feature-gate-specialization.rs:9:5
|
LL | default const CT: () = ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: specialization is experimental
--> $DIR/feature-gate-specialization.rs:10:5
|
LL | default fn fn_(&self) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: specialization is experimental
--> $DIR/feature-gate-specialization.rs:17:1
|
LL | / default impl<T> OtherTrait for T {
LL | | fn fn_() {}
LL | | }
| |_^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.
@@ -0,0 +1,62 @@
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:21:5
|
LL | default type Ty = ();
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:24:5
|
LL | default const CT: () = ();
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:40:1
|
LL | default impl Trait for () {}
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:27:5
|
LL | default fn fn_();
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:35:1
|
LL | default fn fn_() {}
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: 5 warnings emitted
@@ -0,0 +1,38 @@
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:21:5
|
LL | default type Ty = ();
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:24:5
|
LL | default const CT: () = ();
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: specialization is experimental
--> $DIR/soft-feature-gate-specialization.rs:40:1
|
LL | default impl Trait for () {}
| ^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: 3 warnings emitted
@@ -0,0 +1,44 @@
// For historical reasons, item modifier `default` doesn't have a proper pre-expansion feature gate.
// We're now at least issuing a warning for those that only exist before macro expansion.
// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate.
// As part of this, move these test cases into `feature-gate-specialization.rs`.
//
// Moreover, `specialization` implies `min_specialization` similar to the post-expansion gate.
//
// However, while we only gate `default` *associated* functions only behind `min_specialization` OR
// `specialization` in the post-expansion case, in the pre-expansion case we gate all kinds of
// functions (free, assoc, foreign) behind `min_specialization` OR `specialization` if marked with
// `default` for simplicity of implementation. Ultimately it doesn't matter since we later reject
// `default` on anything other than impls & impl assoc items during semantic analysis.
//
//@ revisions: default min full
//@ check-pass
#![cfg_attr(min, feature(min_specialization))]
#![cfg_attr(full, feature(specialization))]
#[cfg(false)]
impl Trait for () {
default type Ty = ();
//[default,min]~^ WARN specialization is experimental
//[default,min]~| WARN unstable syntax can change at any point in the future
default const CT: () = ();
//[default,min]~^ WARN specialization is experimental
//[default,min]~| WARN unstable syntax can change at any point in the future
default fn fn_();
//[default]~^ WARN specialization is experimental
//[default]~| WARN unstable syntax can change at any point in the future
}
// While free ty/ct/fn items marked `default` are
// semantically malformed we still need to gate the keyword!
#[cfg(false)]
default fn fn_() {}
//[default]~^ WARN specialization is experimental
//[default]~| WARN unstable syntax can change at any point in the future
#[cfg(false)]
default impl Trait for () {}
//[default,min]~^ WARN specialization is experimental
//[default,min]~| WARN unstable syntax can change at any point in the future
fn main() {}
@@ -1,13 +0,0 @@
// Check that specialization must be ungated to use the `default` keyword
// gate-test-specialization
trait Foo {
fn foo(&self);
}
impl<T> Foo for T {
default fn foo(&self) {} //~ ERROR specialization is unstable
}
fn main() {}
@@ -1,13 +0,0 @@
error[E0658]: specialization is unstable
--> $DIR/specialization-feature-gate-default.rs:10:5
|
LL | default fn foo(&self) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: add `#![feature(specialization)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.
+1 -1
View File
@@ -12,7 +12,7 @@ pub trait Foo {
impl<T> Foo for T {
default fn bar() {}
//~^ ERROR specialization is unstable
//~^ ERROR specialization is experimental
//~| NOTE created at
}
+1 -1
View File
@@ -1,4 +1,4 @@
error[E0658]: specialization is unstable
error[E0658]: specialization is experimental
--> $DIR/track6.rs:LL:CC
|
LL | default fn bar() {}
@@ -1,3 +1,5 @@
trait MyTrait {}
impl !MyTrait for u32 {} //~ ERROR negative trait bounds are not fully implemented
impl !MyTrait for u32 {} //~ ERROR negative impls are experimental
fn main() {}
@@ -1,5 +1,5 @@
error[E0658]: negative trait bounds are not fully implemented; use marker types for now
--> $DIR/feature-gate-negative_impls.rs:2:6
error[E0658]: negative impls are experimental
--> $DIR/feature-gate-negative_impls.rs:3:6
|
LL | impl !MyTrait for u32 {}
| ^^^^^^^^
@@ -7,6 +7,7 @@ LL | impl !MyTrait for u32 {}
= note: see issue #68318 <https://github.com/rust-lang/rust/issues/68318> for more information
= help: add `#![feature(negative_impls)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= help: use marker types for now
error: aborting due to 1 previous error