about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/collect.rs61
-rw-r--r--src/test/ui/generic-associated-types/type-param-defaults.rs34
-rw-r--r--src/test/ui/generic-associated-types/type-param-defaults.stderr20
3 files changed, 96 insertions, 19 deletions
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 189f1f7e408..45a5eca708a 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1606,6 +1606,13 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
         _ => None,
     };
 
+    enum Defaults {
+        Allowed,
+        // See #36887
+        FutureCompatDisallowed,
+        Deny,
+    }
+
     let no_generics = hir::Generics::empty();
     let ast_generics = node.generics().unwrap_or(&no_generics);
     let (opt_self, allow_defaults) = match node {
@@ -1627,17 +1634,26 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                         },
                     });
 
-                    (opt_self, true)
+                    (opt_self, Defaults::Allowed)
                 }
                 ItemKind::TyAlias(..)
                 | ItemKind::Enum(..)
                 | ItemKind::Struct(..)
                 | ItemKind::OpaqueTy(..)
-                | ItemKind::Union(..) => (None, true),
-                _ => (None, false),
+                | ItemKind::Union(..) => (None, Defaults::Allowed),
+                _ => (None, Defaults::FutureCompatDisallowed),
             }
         }
-        _ => (None, false),
+
+        // GATs
+        Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => {
+            (None, Defaults::Deny)
+        }
+        Node::ImplItem(item) if matches!(item.kind, ImplItemKind::TyAlias(..)) => {
+            (None, Defaults::Deny)
+        }
+
+        _ => (None, Defaults::FutureCompatDisallowed),
     };
 
     let has_self = opt_self.is_some();
@@ -1670,23 +1686,30 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
     let type_start = own_start - has_self as u32 + params.len() as u32;
     let mut i = 0;
 
+    const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
+    `struct`, `enum`, `type`, or `trait` definitions";
+
     params.extend(ast_generics.params.iter().filter_map(|param| match param.kind {
         GenericParamKind::Lifetime { .. } => None,
         GenericParamKind::Type { ref default, synthetic, .. } => {
-            if !allow_defaults && default.is_some() {
-                if !tcx.features().default_type_parameter_fallback {
-                    tcx.struct_span_lint_hir(
-                        lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
-                        param.hir_id,
-                        param.span,
-                        |lint| {
-                            lint.build(
-                                "defaults for type parameters are only allowed in \
-                                 `struct`, `enum`, `type`, or `trait` definitions",
-                            )
-                            .emit();
-                        },
-                    );
+            if default.is_some() {
+                match allow_defaults {
+                    Defaults::Allowed => {}
+                    Defaults::FutureCompatDisallowed
+                        if tcx.features().default_type_parameter_fallback => {}
+                    Defaults::FutureCompatDisallowed => {
+                        tcx.struct_span_lint_hir(
+                            lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
+                            param.hir_id,
+                            param.span,
+                            |lint| {
+                                lint.build(TYPE_DEFAULT_NOT_ALLOWED).emit();
+                            },
+                        );
+                    }
+                    Defaults::Deny => {
+                        tcx.sess.span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED);
+                    }
                 }
             }
 
@@ -1703,7 +1726,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             Some(param_def)
         }
         GenericParamKind::Const { default, .. } => {
-            if !allow_defaults && default.is_some() {
+            if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
                 tcx.sess.span_err(
                     param.span,
                     "defaults for const parameters are only allowed in \
diff --git a/src/test/ui/generic-associated-types/type-param-defaults.rs b/src/test/ui/generic-associated-types/type-param-defaults.rs
new file mode 100644
index 00000000000..f034076b010
--- /dev/null
+++ b/src/test/ui/generic-associated-types/type-param-defaults.rs
@@ -0,0 +1,34 @@
+// Check that we disallow GAT param defaults, even with `invalid_type_param_default` allowed
+
+#![allow(invalid_type_param_default)]
+
+trait Trait {
+    type Assoc<T = u32>;
+    //~^ defaults for type parameters are only allowed
+}
+
+impl Trait for () {
+    type Assoc<T = u32> = u64;
+    //~^ defaults for type parameters are only allowed
+}
+
+impl Trait for u32 {
+    type Assoc<T = u32> = T;
+    //~^ defaults for type parameters are only allowed
+}
+
+trait Other {}
+impl Other for u32 {}
+
+fn foo<T>()
+where
+    T: Trait<Assoc = u32>,
+    T::Assoc: Other {
+    }
+
+fn main() {
+    // errors
+    foo::<()>();
+    // works
+    foo::<u32>();
+}
diff --git a/src/test/ui/generic-associated-types/type-param-defaults.stderr b/src/test/ui/generic-associated-types/type-param-defaults.stderr
new file mode 100644
index 00000000000..85ccaba0e69
--- /dev/null
+++ b/src/test/ui/generic-associated-types/type-param-defaults.stderr
@@ -0,0 +1,20 @@
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/type-param-defaults.rs:6:16
+   |
+LL |     type Assoc<T = u32>;
+   |                ^^^^^^^
+
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/type-param-defaults.rs:11:16
+   |
+LL |     type Assoc<T = u32> = u64;
+   |                ^^^^^^^
+
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/type-param-defaults.rs:16:16
+   |
+LL |     type Assoc<T = u32> = T;
+   |                ^^^^^^^
+
+error: aborting due to 3 previous errors
+