Skip to content

Don't allow opaque ty aliases in ADT fields #86928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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
176 changes: 174 additions & 2 deletions compiler/rustc_typeck/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use rustc_attr as attr;
use rustc_errors::{Applicability, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{def::Res, ItemKind, Node, PathSegment};
use rustc_hir::{def::DefKind, def::Res, ItemKind, Node, PathSegment};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_middle::ty::fold::TypeFoldable;
Expand Down Expand Up @@ -414,7 +414,12 @@ pub(super) fn check_fn<'a, 'tcx>(
}

fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
debug!("check_struct(def_id: {:?}, span: {:?})", def_id, span);

let def = tcx.adt_def(def_id);

check_fields_for_opaque_types(tcx, def, def_id, span);

def.destructor(tcx); // force the destructor to be evaluated
check_representable(tcx, span, def_id);

Expand All @@ -426,8 +431,174 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
check_packed(tcx, span, def);
}

fn check_fields_for_opaque_types(
tcx: TyCtxt<'tcx>,
adt_def: &ty::AdtDef,
def_id: LocalDefId,
span: Span,
) {
struct TyAliasFinder<'tcx> {
tcx: TyCtxt<'tcx>,
ty_alias_span: Option<Span>,
ty_alias_ident: Option<Ident>,
field_ty_span: Option<Span>,
}

impl TyAliasFinder<'_> {
fn new(tcx: TyCtxt<'tcx>) -> TyAliasFinder<'_> {
TyAliasFinder { tcx, ty_alias_span: None, ty_alias_ident: None, field_ty_span: None }
}

fn get_collected_information(&self) -> (Option<Span>, Option<Ident>, Option<Span>) {
(self.field_ty_span, self.ty_alias_ident, self.ty_alias_span)
}
}

impl<'tcx> intravisit::Visitor<'tcx> for TyAliasFinder<'tcx> {
type Map = intravisit::ErasedMap<'tcx>;

fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}

fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = t.kind {
if let Res::Def(DefKind::TyAlias, ty_alias_def_id) = path.res {
let span = path.span;
if let Some(ty_alias_local_id) = ty_alias_def_id.as_local() {
let ty_alias_hir_id =
self.tcx.hir().local_def_id_to_hir_id(ty_alias_local_id);
let node = self.tcx.hir().get(ty_alias_hir_id);
match node {
hir::Node::Item(hir::Item {
ident, kind, span: ty_alias_span, ..
}) => match kind {
hir::ItemKind::TyAlias(_, _) => {
self.ty_alias_span = Some(*ty_alias_span);
self.ty_alias_ident = Some(*ident);
self.field_ty_span = Some(span);
return;
}
_ => bug!("expected an item of kind TyAlias"),
},
_ => {
self.field_ty_span = Some(span);
return;
}
}
}
}
}
intravisit::walk_ty(self, t);
}
}

fn find_ty_alias_information(
tcx: TyCtxt<'tcx>,
field_def: &ty::FieldDef,
) -> (Option<Span>, Option<Span>, Option<Ident>, Option<Span>) {
let field_def_def_id = field_def.did;
if let Some(field_def_local_id) = field_def_def_id.as_local() {
let field_def_hir_id = tcx.hir().local_def_id_to_hir_id(field_def_local_id);
if let hir::Node::Field(hir::FieldDef {
span: field_def_span, ty: field_def_ty, ..
}) = tcx.hir().get(field_def_hir_id)
{
let mut type_alias_finder = TyAliasFinder::new(tcx);
type_alias_finder.visit_ty(field_def_ty);

let (field_ty_span, ty_alias_ident, ty_alias_span) =
type_alias_finder.get_collected_information();
return (Some(*field_def_span), field_ty_span, ty_alias_ident, ty_alias_span);
}
}
(None, None, None, None)
}

if tcx.features().type_alias_impl_trait {
return;
}

debug!("check_fields_of_opaque_types(adt_def: {:?}, span: {:?})", adt_def, span);

let item_type = tcx.type_of(def_id);
let substs = match item_type.kind() {
ty::Adt(_, substs) => substs,
_ => bug!("check_fields_for_opaque_types should only be called on Adts"),
};
adt_def.all_fields().for_each(|field_def| {
debug!("field_def: {:?}", field_def);

let field_ty = field_def.ty(tcx, substs);
if field_ty.has_opaque_types() {
use ty::AdtKind::*;
let adt_kind = match adt_def.adt_kind() {
Struct => "struct",
Enum => "enum",
Union => "union",
};

let mut diag;
match find_ty_alias_information(tcx, field_def) {
(
Some(field_def_span),
Some(field_ty_span),
Some(ty_alias_ident),
Some(ty_alias_span),
) => {
diag = tcx.sess.struct_span_err(
span,
&format!(
"type aliases of `impl Trait` are not allowed as field types in {}s",
adt_kind
),
);
let field_def_span_msg = format!(
"this field contains a type alias `{}` of an `impl Trait`",
ty_alias_ident
);

let field_ty_span_msg =
"this type is a type alias of an `impl Trait`".to_string();

diag.span_label(field_def_span, field_def_span_msg);
diag.span_label(field_ty_span, field_ty_span_msg);
diag.span_label(ty_alias_span, "type alias defined here");
}
(Some(field_def_span), Some(field_ty_span), None, None) => {
diag = tcx.sess.struct_span_err(
span,
&format!(
"type aliases of `impl Trait` are not allowed as field types in {}s",
adt_kind
),
);

diag.span_label(
field_def_span,
"this field contains a type alias of an `impl trait`",
);
diag.span_label(field_ty_span, "this type is a type alias of an `impl trait`");
}
_ => {
diag = tcx.sess.struct_span_err(
span,
&format!(
"type alias impl traits are not allowed as field types in {}s",
adt_kind
),
);
}
}

diag.emit();
}
});
}

fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
let def = tcx.adt_def(def_id);
check_fields_for_opaque_types(tcx, def, def_id, span);
def.destructor(tcx); // force the destructor to be evaluated
check_representable(tcx, span, def_id);
check_transparent(tcx, span, def);
Expand Down Expand Up @@ -1408,6 +1579,7 @@ fn check_enum<'tcx>(
def_id: LocalDefId,
) {
let def = tcx.adt_def(def_id);
check_fields_for_opaque_types(tcx, def, def_id, sp);
def.destructor(tcx); // force the destructor to be evaluated

if vs.is_empty() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type Foo = impl Debug;
//~^ ERROR could not find defining uses

struct Bar(Foo);
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in structs
fn define() -> Bar {
Bar(42) //~ ERROR mismatched types
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/feature-gate-type_alias_impl_trait.rs:10:9
--> $DIR/feature-gate-type_alias_impl_trait.rs:11:9
|
LL | type Foo = impl Debug;
| ---------- the expected opaque type
Expand All @@ -11,7 +11,7 @@ LL | Bar(42)
found type `{integer}`

error[E0658]: type alias impl trait is not permitted here
--> $DIR/feature-gate-type_alias_impl_trait.rs:16:19
--> $DIR/feature-gate-type_alias_impl_trait.rs:17:19
|
LL | let x = || -> Foo2 { 42 };
| ^^^^
Expand All @@ -20,7 +20,7 @@ LL | let x = || -> Foo2 { 42 };
= help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable

error[E0308]: mismatched types
--> $DIR/feature-gate-type_alias_impl_trait.rs:23:18
--> $DIR/feature-gate-type_alias_impl_trait.rs:24:18
|
LL | type Foo3 = impl Debug;
| ---------- the found opaque type
Expand All @@ -34,7 +34,7 @@ LL | let y: i32 = x;
found opaque type `impl Debug`

error[E0308]: mismatched types
--> $DIR/feature-gate-type_alias_impl_trait.rs:26:13
--> $DIR/feature-gate-type_alias_impl_trait.rs:27:13
|
LL | type Foo3 = impl Debug;
| ---------- the expected opaque type
Expand All @@ -46,7 +46,7 @@ LL | define3(42)
found type `{integer}`

error[E0658]: type alias impl trait is not permitted here
--> $DIR/feature-gate-type_alias_impl_trait.rs:33:12
--> $DIR/feature-gate-type_alias_impl_trait.rs:34:12
|
LL | let y: Foo4 = 42;
| ^^^^
Expand All @@ -60,19 +60,31 @@ error: could not find defining uses
LL | type Foo = impl Debug;
| ^^^^^^^^^^

error: type aliases of `impl Trait` are not allowed as field types in structs
--> $DIR/feature-gate-type_alias_impl_trait.rs:8:1
|
LL | type Foo = impl Debug;
| ---------------------- type alias defined here
...
LL | struct Bar(Foo);
| ^^^^^^^^^^^---^^
| |
| this field contains a type alias `Foo` of an `impl Trait`
| this type is a type alias of an `impl Trait`

error: could not find defining uses
--> $DIR/feature-gate-type_alias_impl_trait.rs:19:13
--> $DIR/feature-gate-type_alias_impl_trait.rs:20:13
|
LL | type Foo3 = impl Debug;
| ^^^^^^^^^^

error: could not find defining uses
--> $DIR/feature-gate-type_alias_impl_trait.rs:29:13
--> $DIR/feature-gate-type_alias_impl_trait.rs:30:13
|
LL | type Foo4 = impl Debug;
| ^^^^^^^^^^

error: aborting due to 8 previous errors
error: aborting due to 9 previous errors

Some errors have detailed explanations: E0308, E0658.
For more information about an error, try `rustc --explain E0308`.
45 changes: 45 additions & 0 deletions src/test/ui/impl-trait/reject-opaque_types-in-fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![feature(min_type_alias_impl_trait)]

type ImplCopy = impl Copy;
//~^ ERROR could not find defining uses

enum Wrapper {
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in enums
First(ImplCopy),
Second
}

type X = impl Iterator<Item = u64> + Unpin;
//~^ ERROR could not find defining uses

struct Foo(X);
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in structs

impl Foo {
fn new(z: Vec<u64>) -> Self {
Foo(z.into_iter())
//~^ ERROR mismatched types
}
}

struct FooNested(Vec<X>);
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in structs

struct Bar {a : X}
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in structs

impl Bar {
fn new(z: Vec<u64>) -> Self {
Bar {a: z.into_iter() }
//~^ ERROR mismatched types
}
}

union MyUnion {
//~^ ERROR type aliases of `impl Trait` are not allowed as field types in unions
a: X,
//~^ ERROR unions may not contain fields that need dropping
}


fn main() {}
Loading