Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2174,28 +2174,51 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Case 2. Reference to a variant constructor.
DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => {
let (generics_def_id, index) = if let Some(self_ty) = self_ty {
// We have something like `<module::Enum>::Variant`.

let adt_def = self.probe_adt(span, self_ty).unwrap();
debug_assert!(adt_def.is_enum());

// FIXME: Stating that the last segment (here: `Variant`) is allowed to have
// generic args is a lie! We should set the index to `None` instead as it's
// the *self type* that's allowed to have args.
// HIR typeck's `instantiate_value_path` actually contains a special case to
// reject args on `DefKind::Ctor` segments (see `is_alias_variant_ctor`).
// Using `None` here for this should allow us to get rid of that workaround.
//
// (For additional context, `DefKind::Variant` segments never actually reach
// this branch as they're interpreted as `TypeRelative` paths whose lowering
// routines manually reject args on them).

(adt_def.did(), last)
} else if last >= 1 && segments[last - 1].args.is_some() {
// Everything but the penultimate segment should have no
// parameters at all.
let mut def_id = def_id;
} else if let [.., second_to_last, _] = segments
&& second_to_last.args.is_some()
&& let Res::Def(DefKind::Enum, _) = second_to_last.res
{
// We have something like `module::Enum::<…>::Variant`.
// No segment other than the penultimate one is allowed to have generic args.

// We had to check that the second to last segment actually referred to an enum
// since at this stage it could very well refer to a module in which case we
// certainly don't want to allow generic args on it!

// `DefKind::Ctor` -> `DefKind::Variant`
if let DefKind::Ctor(..) = kind {
def_id = tcx.parent(def_id);
}
let def_id = match kind {
DefKind::Ctor(..) => tcx.parent(def_id),
_ => def_id,
};

// `DefKind::Variant` -> `DefKind::Enum`
let enum_def_id = tcx.parent(def_id);

(enum_def_id, last - 1)
} else {
// We have something like `module::Enum::Variant` or `module::Variant`.
// No segment other than the final one is allowed to have generic args.

// FIXME: lint here recommending `Enum::<...>::Variant` form
// instead of `Enum::Variant::<...>` form.

// Everything but the final segment should have no
// parameters at all.
let generics = tcx.generics_of(def_id);
// Variant and struct constructors use the
// generics of their parent type definition.
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/enum/enum-variant-generic-args-in-module-segment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Test that we don't assume that the penultimate segment of an enum variant path refers to the
// enum just because it has generic arguments.
//
// We once used to accept the code below interpreting `module::<i32>::Variant`
// as `module::Variant::<i32>` basically.
//
// issue: <https://github.com/rust-lang/rust/issues/154962>

enum Enum<T> { Variant, Carry(T) }

mod module { pub(super) use super::Enum::Variant; }

fn main() {
let _ = module::<i32>::Variant;
//~^ ERROR type arguments are not allowed on module `module`

let _ = self::module::<()>::Variant {};
//~^ ERROR type arguments are not allowed on module `module`
}
19 changes: 19 additions & 0 deletions tests/ui/enum/enum-variant-generic-args-in-module-segment.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0109]: type arguments are not allowed on module `module`
--> $DIR/enum-variant-generic-args-in-module-segment.rs:14:22
|
LL | let _ = module::<i32>::Variant;
| ------ ^^^ type argument not allowed
| |
| not allowed on module `module`

error[E0109]: type arguments are not allowed on module `module`
--> $DIR/enum-variant-generic-args-in-module-segment.rs:17:28
|
LL | let _ = self::module::<()>::Variant {};
| ------ ^^ type argument not allowed
| |
| not allowed on module `module`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0109`.
Loading