Disallow defaults on type GATs

This commit is contained in:
Jack Huey 2022-09-14 10:02:48 -04:00
parent c97922dca5
commit d657d1f4a1
3 changed files with 96 additions and 19 deletions

View File

@ -1604,6 +1604,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 {
@ -1625,17 +1632,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();
@ -1668,23 +1684,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);
}
}
}
@ -1701,7 +1724,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 \

View File

@ -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>();
}

View File

@ -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