summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-06-09 08:06:13 +0000
committerbors <bors@rust-lang.org>2018-06-09 08:06:13 +0000
commitc859c4f1a491a56efddedbb6ee76d2073232e7da (patch)
treea2801335132a17fde3e7d4f53ffd06dce62406af
parenteb01d0fc0e8109c5ec4d176a09029386e6f630df (diff)
parent6bf3078f4126a9c1fadc1365ae781e00691cd741 (diff)
downloadrust-c859c4f1a491a56efddedbb6ee76d2073232e7da.tar.gz
rust-c859c4f1a491a56efddedbb6ee76d2073232e7da.zip
Auto merge of #51432 - pietroalbini:beta-backports, r=kennytm
[beta] Rollup backports

Merged and accepted:

* #51417: Revert #49719
* #51283: Deny #[cfg] and #[cfg_attr] on generic parameters
* #51328: Do not promote union field accesses

r? @ghost
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs10
-rw-r--r--src/librustc_passes/rvalue_promotion.rs11
-rw-r--r--src/libsyntax/attr.rs20
-rw-r--r--src/libsyntax/config.rs16
-rw-r--r--src/libsyntax/ext/expand.rs5
-rw-r--r--src/libsyntax/ext/tt/quoted.rs89
-rw-r--r--src/libsyntax/feature_gate.rs2
-rw-r--r--src/test/run-pass/ctfe/union-ice.rs45
-rw-r--r--src/test/run-pass/macro-at-most-once-rep.rs29
-rw-r--r--src/test/run-pass/union/union-const-eval-field.rs14
-rw-r--r--src/test/ui/const-eval/union_promotion.rs22
-rw-r--r--src/test/ui/const-eval/union_promotion.stderr16
-rw-r--r--src/test/ui/issue-51279.rs34
-rw-r--r--src/test/ui/issue-51279.stderr50
-rw-r--r--src/test/ui/macros/macro-at-most-once-rep-ambig.rs34
-rw-r--r--src/test/ui/macros/macro-at-most-once-rep-ambig.stderr76
16 files changed, 346 insertions, 127 deletions
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 14dedcb0c34..fcfa860f25e 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -556,8 +556,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
                         ProjectionElem::Field(..) |
                         ProjectionElem::Index(_) => {
-                            if this.mode != Mode::Fn &&
-                               this.qualif.intersects(Qualif::STATIC) {
+                            if this.mode == Mode::Fn {
+                                let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
+                                if let Some(def) = base_ty.ty_adt_def() {
+                                    if def.is_union() {
+                                        this.not_const();
+                                    }
+                                }
+                            } else if this.qualif.intersects(Qualif::STATIC) {
                                 span_err!(this.tcx.sess, this.span, E0494,
                                           "cannot refer to the interior of another \
                                            static, use a constant instead");
diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs
index d4fa47c5b48..3a05986a1d4 100644
--- a/src/librustc_passes/rvalue_promotion.rs
+++ b/src/librustc_passes/rvalue_promotion.rs
@@ -424,9 +424,16 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
             }
         }
 
-        hir::ExprBlock(_) |
+        hir::ExprField(ref expr, _) => {
+            if let Some(def) = v.tables.expr_ty(expr).ty_adt_def() {
+                if def.is_union() {
+                    v.promotable = false
+                }
+            }
+        }
+
+        hir::ExprBlock(..) |
         hir::ExprIndex(..) |
-        hir::ExprField(..) |
         hir::ExprArray(_) |
         hir::ExprType(..) |
         hir::ExprTup(..) => {}
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index ace9904e0c0..c70f5749202 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -17,7 +17,7 @@ pub use self::IntType::*;
 use ast;
 use ast::{AttrId, Attribute, Name, Ident, Path, PathSegment};
 use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
-use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind};
+use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind, GenericParam};
 use codemap::{BytePos, Spanned, respan, dummy_spanned};
 use syntax_pos::Span;
 use errors::Handler;
@@ -1438,6 +1438,22 @@ impl HasAttrs for Stmt {
     }
 }
 
+impl HasAttrs for GenericParam {
+    fn attrs(&self) -> &[ast::Attribute] {
+        match self {
+            GenericParam::Lifetime(lifetime) => lifetime.attrs(),
+            GenericParam::Type(ty) => ty.attrs(),
+        }
+    }
+
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        match self {
+            GenericParam::Lifetime(lifetime) => GenericParam::Lifetime(lifetime.map_attrs(f)),
+            GenericParam::Type(ty) => GenericParam::Type(ty.map_attrs(f)),
+        }
+    }
+}
+
 macro_rules! derive_has_attrs {
     ($($ty:path),*) => { $(
         impl HasAttrs for $ty {
@@ -1457,5 +1473,5 @@ macro_rules! derive_has_attrs {
 
 derive_has_attrs! {
     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
-    ast::Field, ast::FieldPat, ast::Variant_
+    ast::Field, ast::FieldPat, ast::Variant_, ast::LifetimeDef, ast::TyParam
 }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 36911683a0e..33643789139 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -278,6 +278,22 @@ impl<'a> StripUnconfigured<'a> {
             pattern
         })
     }
+
+    // deny #[cfg] on generic parameters until we decide what to do with it.
+    // see issue #51279.
+    pub fn disallow_cfg_on_generic_param(&mut self, param: &ast::GenericParam) {
+        for attr in param.attrs() {
+            let offending_attr = if attr.check_name("cfg") {
+                "cfg"
+            } else if attr.check_name("cfg_attr") {
+                "cfg_attr"
+            } else {
+                continue;
+            };
+            let msg = format!("#[{}] cannot be applied on a generic parameter", offending_attr);
+            self.sess.span_diagnostic.span_err(attr.span, &msg);
+        }
+    }
 }
 
 impl<'a> fold::Folder for StripUnconfigured<'a> {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 584b9455a93..8fee4881cca 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1350,6 +1350,11 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
         }
     }
 
+    fn fold_generic_param(&mut self, param: ast::GenericParam) -> ast::GenericParam {
+        self.cfg.disallow_cfg_on_generic_param(&param);
+        noop_fold_generic_param(param, self)
+    }
+
     fn fold_attribute(&mut self, at: ast::Attribute) -> Option<ast::Attribute> {
         // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename",
         // contents="file contents")]` attributes
diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs
index 77c6afa1c64..01b971976a7 100644
--- a/src/libsyntax/ext/tt/quoted.rs
+++ b/src/libsyntax/ext/tt/quoted.rs
@@ -386,26 +386,72 @@ where
 {
     // We basically look at two token trees here, denoted as #1 and #2 below
     let span = match parse_kleene_op(input, span) {
-        // #1 is any KleeneOp (`?`)
-        Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
-            if !features.macro_at_most_once_rep
-                && !attr::contains_name(attrs, "allow_internal_unstable")
-            {
-                let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
-                emit_feature_err(
-                    sess,
-                    "macro_at_most_once_rep",
-                    span,
-                    GateIssue::Language,
-                    explain,
-                );
+        // #1 is a `+` or `*` KleeneOp
+        //
+        // `?` is ambiguous: it could be a separator or a Kleene::ZeroOrOne, so we need to look
+        // ahead one more token to be sure.
+        Ok(Ok(op)) if op != KleeneOp::ZeroOrOne => return (None, op),
+
+        // #1 is `?` token, but it could be a Kleene::ZeroOrOne without a separator or it could
+        // be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to
+        // find out which.
+        Ok(Ok(op)) => {
+            assert_eq!(op, KleeneOp::ZeroOrOne);
+
+            // Lookahead at #2. If it is a KleenOp, then #1 is a separator.
+            let is_1_sep = if let Some(&tokenstream::TokenTree::Token(_, ref tok2)) = input.peek() {
+                kleene_op(tok2).is_some()
+            } else {
+                false
+            };
+
+            if is_1_sep {
+                // #1 is a separator and #2 should be a KleepeOp::*
+                // (N.B. We need to advance the input iterator.)
+                match parse_kleene_op(input, span) {
+                    // #2 is a KleeneOp (this is the only valid option) :)
+                    Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
+                        if !features.macro_at_most_once_rep
+                            && !attr::contains_name(attrs, "allow_internal_unstable")
+                        {
+                            let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
+                            emit_feature_err(
+                                sess,
+                                "macro_at_most_once_rep",
+                                span,
+                                GateIssue::Language,
+                                explain,
+                            );
+                        }
+                        return (Some(token::Question), op);
+                    }
+                    Ok(Ok(op)) => return (Some(token::Question), op),
+
+                    // #2 is a random token (this is an error) :(
+                    Ok(Err((_, span))) => span,
+
+                    // #2 is not even a token at all :(
+                    Err(span) => span,
+                }
+            } else {
+                if !features.macro_at_most_once_rep
+                    && !attr::contains_name(attrs, "allow_internal_unstable")
+                {
+                    let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
+                    emit_feature_err(
+                        sess,
+                        "macro_at_most_once_rep",
+                        span,
+                        GateIssue::Language,
+                        explain,
+                    );
+                }
+
+                // #2 is a random tree and #1 is KleeneOp::ZeroOrOne
+                return (None, op);
             }
-            return (None, op);
         }
 
-        // #1 is any KleeneOp (`+`, `*`)
-        Ok(Ok(op)) => return (None, op),
-
         // #1 is a separator followed by #2, a KleeneOp
         Ok(Err((tok, span))) => match parse_kleene_op(input, span) {
             // #2 is a KleeneOp :D
@@ -421,11 +467,8 @@ where
                         GateIssue::Language,
                         explain,
                     );
-                } else {
-                    sess.span_diagnostic
-                        .span_err(span, "`?` macro repetition does not allow a separator");
                 }
-                return (None, op);
+                return (Some(tok), op);
             }
             Ok(Ok(op)) => return (Some(tok), op),
 
@@ -440,7 +483,9 @@ where
         Err(span) => span,
     };
 
-    if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") {
+    if !features.macro_at_most_once_rep
+        && !attr::contains_name(attrs, "allow_internal_unstable")
+    {
         sess.span_diagnostic
             .span_err(span, "expected one of: `*`, `+`, or `?`");
     } else {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9838a11372b..7f8c361525f 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -586,7 +586,7 @@ declare_features! (
     // allow `'_` placeholder lifetimes
     (accepted, underscore_lifetimes, "1.26.0", Some(44524), None),
     // Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
-    (accepted, generic_param_attrs, "1.26.0", Some(48848), None),
+    (accepted, generic_param_attrs, "1.27.0", Some(48848), None),
     // Allows cfg(target_feature = "...").
     (accepted, cfg_target_feature, "1.27.0", Some(29717), None),
     // Allows #[target_feature(...)]
diff --git a/src/test/run-pass/ctfe/union-ice.rs b/src/test/run-pass/ctfe/union-ice.rs
deleted file mode 100644
index f83f49f298b..00000000000
--- a/src/test/run-pass/ctfe/union-ice.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![feature(const_fn)]
-
-type Field1 = i32;
-type Field2 = f32;
-type Field3 = i64;
-
-union DummyUnion {
-    field1: Field1,
-    field2: Field2,
-    field3: Field3,
-}
-
-const FLOAT1_AS_I32: i32 = 1065353216;
-const UNION: DummyUnion = DummyUnion { field1: FLOAT1_AS_I32 };
-
-const fn read_field1() -> Field1 {
-    const FIELD1: Field1 = unsafe { UNION.field1 };
-    FIELD1
-}
-
-const fn read_field2() -> Field2 {
-    const FIELD2: Field2 = unsafe { UNION.field2 };
-    FIELD2
-}
-
-const fn read_field3() -> Field3 {
-    const FIELD3: Field3 = unsafe { UNION.field3 };
-    FIELD3
-}
-
-fn main() {
-    assert_eq!(read_field1(), FLOAT1_AS_I32);
-    assert_eq!(read_field2(), 1.0);
-    assert_eq!(read_field3(), unsafe { UNION.field3 });
-}
diff --git a/src/test/run-pass/macro-at-most-once-rep.rs b/src/test/run-pass/macro-at-most-once-rep.rs
index c08effe5493..b7e942f9383 100644
--- a/src/test/run-pass/macro-at-most-once-rep.rs
+++ b/src/test/run-pass/macro-at-most-once-rep.rs
@@ -32,13 +32,25 @@ macro_rules! foo {
     } }
 }
 
+macro_rules! baz {
+    ($($a:ident),? ; $num:expr) => { { // comma separator is meaningless for `?`
+        let mut x = 0;
+
+        $(
+            x += $a;
+         )?
+
+        assert_eq!(x, $num);
+    } }
+}
+
 macro_rules! barplus {
     ($($a:ident)?+ ; $num:expr) => { {
         let mut x = 0;
 
         $(
             x += $a;
-         )?
+         )+
 
         assert_eq!(x, $num);
     } }
@@ -50,7 +62,7 @@ macro_rules! barstar {
 
         $(
             x += $a;
-         )?
+         )*
 
         assert_eq!(x, $num);
     } }
@@ -62,10 +74,15 @@ pub fn main() {
     // accept 0 or 1 repetitions
     foo!( ; 0);
     foo!(a ; 1);
+    baz!( ; 0);
+    baz!(a ; 1);
 
     // Make sure using ? as a separator works as before
-    barplus!(+ ; 0);
-    barplus!(a + ; 1);
-    barstar!(* ; 0);
-    barstar!(a * ; 1);
+    barplus!(a ; 1);
+    barplus!(a?a ; 2);
+    barplus!(a?a?a ; 3);
+    barstar!( ; 0);
+    barstar!(a ; 1);
+    barstar!(a?a ; 2);
+    barstar!(a?a?a ; 3);
 }
diff --git a/src/test/run-pass/union/union-const-eval-field.rs b/src/test/run-pass/union/union-const-eval-field.rs
index f83f49f298b..a380b01dcc1 100644
--- a/src/test/run-pass/union/union-const-eval-field.rs
+++ b/src/test/run-pass/union/union-const-eval-field.rs
@@ -10,7 +10,7 @@
 
 #![feature(const_fn)]
 
-type Field1 = i32;
+type Field1 = (i32, u32);
 type Field2 = f32;
 type Field3 = i64;
 
@@ -21,7 +21,7 @@ union DummyUnion {
 }
 
 const FLOAT1_AS_I32: i32 = 1065353216;
-const UNION: DummyUnion = DummyUnion { field1: FLOAT1_AS_I32 };
+const UNION: DummyUnion = DummyUnion { field1: (FLOAT1_AS_I32, 0) };
 
 const fn read_field1() -> Field1 {
     const FIELD1: Field1 = unsafe { UNION.field1 };
@@ -39,7 +39,15 @@ const fn read_field3() -> Field3 {
 }
 
 fn main() {
-    assert_eq!(read_field1(), FLOAT1_AS_I32);
+    let foo = FLOAT1_AS_I32;
+    assert_eq!(read_field1().0, foo);
+    assert_eq!(read_field1().0, FLOAT1_AS_I32);
+
+    let foo = 1.0;
+    assert_eq!(read_field2(), foo);
     assert_eq!(read_field2(), 1.0);
+
     assert_eq!(read_field3(), unsafe { UNION.field3 });
+    let foo = unsafe { UNION.field3 };
+    assert_eq!(read_field3(), foo);
 }
diff --git a/src/test/ui/const-eval/union_promotion.rs b/src/test/ui/const-eval/union_promotion.rs
new file mode 100644
index 00000000000..714d7a4fc8b
--- /dev/null
+++ b/src/test/ui/const-eval/union_promotion.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(const_err)]
+
+union Foo {
+    a: &'static u32,
+    b: usize,
+}
+
+fn main() {
+    let x: &'static bool = &unsafe { //~ borrowed value does not live long enough
+        Foo { a: &1 }.b == Foo { a: &2 }.b
+    };
+}
diff --git a/src/test/ui/const-eval/union_promotion.stderr b/src/test/ui/const-eval/union_promotion.stderr
new file mode 100644
index 00000000000..b4aa91f2de7
--- /dev/null
+++ b/src/test/ui/const-eval/union_promotion.stderr
@@ -0,0 +1,16 @@
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/union_promotion.rs:19:29
+   |
+LL |       let x: &'static bool = &unsafe { //~ borrowed value does not live long enough
+   |  _____________________________^
+LL | |         Foo { a: &1 }.b == Foo { a: &2 }.b
+LL | |     };
+   | |_____^ temporary value does not live long enough
+LL |   }
+   |   - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/issue-51279.rs b/src/test/ui/issue-51279.rs
new file mode 100644
index 00000000000..4639d73e44d
--- /dev/null
+++ b/src/test/ui/issue-51279.rs
@@ -0,0 +1,34 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct X<#[cfg(none)] 'a, #[cfg(none)] T>(&'a T);
+//~^ ERROR #[cfg] cannot be applied on a generic parameter
+//~^^ ERROR #[cfg] cannot be applied on a generic parameter
+
+impl<#[cfg(none)] 'a, #[cfg(none)] T> X<'a, T> {}
+//~^ ERROR #[cfg] cannot be applied on a generic parameter
+//~^^ ERROR #[cfg] cannot be applied on a generic parameter
+
+pub fn f<#[cfg(none)] 'a, #[cfg(none)] T>(_: &'a T) {}
+//~^ ERROR #[cfg] cannot be applied on a generic parameter
+//~^^ ERROR #[cfg] cannot be applied on a generic parameter
+
+#[cfg(none)]
+pub struct Y<#[cfg(none)] T>(T); // shouldn't care when the entire item is stripped out
+
+struct M<T>(*const T);
+
+unsafe impl<#[cfg_attr(none, may_dangle)] T> Drop for M<T> {
+    //~^ ERROR #[cfg_attr] cannot be applied on a generic parameter
+    fn drop(&mut self) {}
+}
+
+type Z<#[ignored] 'a, #[cfg(none)] T> = X<'a, T>;
+//~^ ERROR #[cfg] cannot be applied on a generic parameter
diff --git a/src/test/ui/issue-51279.stderr b/src/test/ui/issue-51279.stderr
new file mode 100644
index 00000000000..38d5a5acc50
--- /dev/null
+++ b/src/test/ui/issue-51279.stderr
@@ -0,0 +1,50 @@
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:11:14
+   |
+LL | pub struct X<#[cfg(none)] 'a, #[cfg(none)] T>(&'a T);
+   |              ^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:11:31
+   |
+LL | pub struct X<#[cfg(none)] 'a, #[cfg(none)] T>(&'a T);
+   |                               ^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:15:6
+   |
+LL | impl<#[cfg(none)] 'a, #[cfg(none)] T> X<'a, T> {}
+   |      ^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:15:23
+   |
+LL | impl<#[cfg(none)] 'a, #[cfg(none)] T> X<'a, T> {}
+   |                       ^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:19:10
+   |
+LL | pub fn f<#[cfg(none)] 'a, #[cfg(none)] T>(_: &'a T) {}
+   |          ^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:19:27
+   |
+LL | pub fn f<#[cfg(none)] 'a, #[cfg(none)] T>(_: &'a T) {}
+   |                           ^^^^^^^^^^^^
+
+error: #[cfg_attr] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:28:13
+   |
+LL | unsafe impl<#[cfg_attr(none, may_dangle)] T> Drop for M<T> {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: #[cfg] cannot be applied on a generic parameter
+  --> $DIR/issue-51279.rs:33:23
+   |
+LL | type Z<#[ignored] 'a, #[cfg(none)] T> = X<'a, T>;
+   |                       ^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/test/ui/macros/macro-at-most-once-rep-ambig.rs b/src/test/ui/macros/macro-at-most-once-rep-ambig.rs
index e25c3ccfcd9..a5660f8b41f 100644
--- a/src/test/ui/macros/macro-at-most-once-rep-ambig.rs
+++ b/src/test/ui/macros/macro-at-most-once-rep-ambig.rs
@@ -8,26 +8,30 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Tests the behavior of various Kleene operators in macros with respect to `?` terminals. In
-// particular, `?` in the position of a separator and of a Kleene operator is tested.
+// The logic for parsing Kleene operators in macros has a special case to disambiguate `?`.
+// Specifically, `$(pat)?` is the ZeroOrOne operator whereas `$(pat)?+` or `$(pat)?*` are the
+// ZeroOrMore and OneOrMore operators using `?` as a separator. These tests are intended to
+// exercise that logic in the macro parser.
+//
+// Moreover, we also throw in some tests for using a separator with `?`, which is meaningless but
+// included for consistency with `+` and `*`.
+//
+// This test focuses on error cases.
 
 #![feature(macro_at_most_once_rep)]
 
-// should match `` and `a`
 macro_rules! foo {
     ($(a)?) => {}
 }
 
 macro_rules! baz {
-    ($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
+    ($(a),?) => {} // comma separator is meaningless for `?`
 }
 
-// should match `+` and `a+`
 macro_rules! barplus {
     ($(a)?+) => {}
 }
 
-// should match `*` and `a*`
 macro_rules! barstar {
     ($(a)?*) => {}
 }
@@ -36,14 +40,14 @@ pub fn main() {
     foo!(a?a?a); //~ ERROR no rules expected the token `?`
     foo!(a?a); //~ ERROR no rules expected the token `?`
     foo!(a?); //~ ERROR no rules expected the token `?`
+    baz!(a?a?a); //~ ERROR no rules expected the token `?`
+    baz!(a?a); //~ ERROR no rules expected the token `?`
+    baz!(a?); //~ ERROR no rules expected the token `?`
+    baz!(a,); //~ ERROR unexpected end of macro invocation
+    baz!(a?a?a,); //~ ERROR no rules expected the token `?`
+    baz!(a?a,); //~ ERROR no rules expected the token `?`
+    baz!(a?,); //~ ERROR no rules expected the token `?`
     barplus!(); //~ ERROR unexpected end of macro invocation
-    barstar!(); //~ ERROR unexpected end of macro invocation
-    barplus!(a?); //~ ERROR no rules expected the token `?`
-    barplus!(a); //~ ERROR unexpected end of macro invocation
-    barstar!(a?); //~ ERROR no rules expected the token `?`
-    barstar!(a); //~ ERROR unexpected end of macro invocation
-    barplus!(+); // ok
-    barstar!(*); // ok
-    barplus!(a+); // ok
-    barstar!(a*); // ok
+    barplus!(a?); //~ ERROR unexpected end of macro invocation
+    barstar!(a?); //~ ERROR unexpected end of macro invocation
 }
diff --git a/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr
index cb1e360471c..d382082a575 100644
--- a/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr
+++ b/src/test/ui/macros/macro-at-most-once-rep-ambig.stderr
@@ -1,62 +1,80 @@
-error: `?` macro repetition does not allow a separator
-  --> $DIR/macro-at-most-once-rep-ambig.rs:22:10
-   |
-LL |     ($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
-   |          ^
-
 error: no rules expected the token `?`
-  --> $DIR/macro-at-most-once-rep-ambig.rs:36:11
+  --> $DIR/macro-at-most-once-rep-ambig.rs:40:11
    |
 LL |     foo!(a?a?a); //~ ERROR no rules expected the token `?`
    |           ^
 
 error: no rules expected the token `?`
-  --> $DIR/macro-at-most-once-rep-ambig.rs:37:11
+  --> $DIR/macro-at-most-once-rep-ambig.rs:41:11
    |
 LL |     foo!(a?a); //~ ERROR no rules expected the token `?`
    |           ^
 
 error: no rules expected the token `?`
-  --> $DIR/macro-at-most-once-rep-ambig.rs:38:11
+  --> $DIR/macro-at-most-once-rep-ambig.rs:42:11
    |
 LL |     foo!(a?); //~ ERROR no rules expected the token `?`
    |           ^
 
-error: unexpected end of macro invocation
-  --> $DIR/macro-at-most-once-rep-ambig.rs:39:5
+error: no rules expected the token `?`
+  --> $DIR/macro-at-most-once-rep-ambig.rs:43:11
    |
-LL |     barplus!(); //~ ERROR unexpected end of macro invocation
-   |     ^^^^^^^^^^^
+LL |     baz!(a?a?a); //~ ERROR no rules expected the token `?`
+   |           ^
 
-error: unexpected end of macro invocation
-  --> $DIR/macro-at-most-once-rep-ambig.rs:40:5
+error: no rules expected the token `?`
+  --> $DIR/macro-at-most-once-rep-ambig.rs:44:11
    |
-LL |     barstar!(); //~ ERROR unexpected end of macro invocation
-   |     ^^^^^^^^^^^
+LL |     baz!(a?a); //~ ERROR no rules expected the token `?`
+   |           ^
 
 error: no rules expected the token `?`
-  --> $DIR/macro-at-most-once-rep-ambig.rs:41:15
+  --> $DIR/macro-at-most-once-rep-ambig.rs:45:11
    |
-LL |     barplus!(a?); //~ ERROR no rules expected the token `?`
-   |               ^
+LL |     baz!(a?); //~ ERROR no rules expected the token `?`
+   |           ^
 
 error: unexpected end of macro invocation
-  --> $DIR/macro-at-most-once-rep-ambig.rs:42:14
+  --> $DIR/macro-at-most-once-rep-ambig.rs:46:11
+   |
+LL |     baz!(a,); //~ ERROR unexpected end of macro invocation
+   |           ^
+
+error: no rules expected the token `?`
+  --> $DIR/macro-at-most-once-rep-ambig.rs:47:11
+   |
+LL |     baz!(a?a?a,); //~ ERROR no rules expected the token `?`
+   |           ^
+
+error: no rules expected the token `?`
+  --> $DIR/macro-at-most-once-rep-ambig.rs:48:11
    |
-LL |     barplus!(a); //~ ERROR unexpected end of macro invocation
-   |              ^
+LL |     baz!(a?a,); //~ ERROR no rules expected the token `?`
+   |           ^
 
 error: no rules expected the token `?`
-  --> $DIR/macro-at-most-once-rep-ambig.rs:43:15
+  --> $DIR/macro-at-most-once-rep-ambig.rs:49:11
+   |
+LL |     baz!(a?,); //~ ERROR no rules expected the token `?`
+   |           ^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-at-most-once-rep-ambig.rs:50:5
+   |
+LL |     barplus!(); //~ ERROR unexpected end of macro invocation
+   |     ^^^^^^^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-at-most-once-rep-ambig.rs:51:15
    |
-LL |     barstar!(a?); //~ ERROR no rules expected the token `?`
+LL |     barplus!(a?); //~ ERROR unexpected end of macro invocation
    |               ^
 
 error: unexpected end of macro invocation
-  --> $DIR/macro-at-most-once-rep-ambig.rs:44:14
+  --> $DIR/macro-at-most-once-rep-ambig.rs:52:15
    |
-LL |     barstar!(a); //~ ERROR unexpected end of macro invocation
-   |              ^
+LL |     barstar!(a?); //~ ERROR unexpected end of macro invocation
+   |               ^
 
-error: aborting due to 10 previous errors
+error: aborting due to 13 previous errors