Change:
cc Gives the following incorrect warning:
I'm not sure if this is incorrect - I think this is what I'd expect. In the declaration of , we don't know what concrete type is - if it were , then we could normalize to - but we don't know? Maybe I'm missing something.
Hmm, that might be true. I definitely think that the following should not lint here (with bound to u32)
I agree that seems like it should be fine. So, this happens because of this call: It makes sense that we want to lint on opaque types on extern declarations, it's unclear whether we would want to allow projections which contain opaque types - this would solve the case you've shown here, but could be used to "sneak" opaque types by the lint (in , the projection normalizes to an opaque type; it's hard to detect because the normalization goes further and resolves it to so we can't tell that it was an opaque type) - I have a build with this locally. Any thoughts on whether we should change the behavior here? cc
Hm, I think it might be worth it to normalize using here, which should not normalize the first case and your example while fixing the second one if I am not mistaken.
Long ago I filed where we also concluded Reveal::UserFacing is the right behavior for normalization in this lint. The current behavior comes from cc
I'll put up a PR with my experiments from last night later in that case.

Discussion:
} fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { <del> struct ProhibitOpaqueTypes<'tcx> { </del> <ins> struct ProhibitOpaqueTypes<'a, 'tcx> { cx: &'a LateContext<'a, 'tcx>, </ins> ty: Option<Ty<'tcx>>, }; <del> impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'tcx> { </del> <ins> impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { </ins> fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { <del> if let ty::Opaque(..) = ty.kind { self.ty = Some(ty); true } else { ty.super_visit_with(self) </del> <ins> match ty.kind { ty::Opaque(..) => { self.ty = Some(ty); true } // Consider opaque types within projections FFI-safe if they do not normalize // to more opaque types. ty::Projection(..) => { let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); // If `ty` is a opaque type directly then `super_visit_with` won't invoke // this function again. if ty.has_opaque_types() { self.visit_ty(ty) } else { false } } _ => ty.super_visit_with(self), </ins> } } } <del> let mut visitor = ProhibitOpaqueTypes { ty: None }; </del> <ins> let mut visitor = ProhibitOpaqueTypes { cx: self.cx, ty: None }; </ins> ty.visit_with(&mut visitor); if let Some(ty) = visitor.ty { self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None);

<ins> #![feature(type_alias_impl_trait)] #![deny(improper_ctypes)] pub trait Baz { } impl Baz for u32 { } type Qux = impl Baz; pub trait Foo { type Assoc; } impl Foo for u32 { type Assoc = Qux; } fn assign() -> Qux { 1 } extern "C" { pub fn lint_me() -> <u32 as Foo>::Assoc; //~ ERROR: uses type `impl Baz` } fn main() {} </ins>

<ins> error: `extern` block uses type `impl Baz`, which is not FFI-safe --> $DIR/lint-ctypes-73251-1.rs:21:25 | LL |     pub fn lint_me() -> <u32 as Foo>::Assoc; |                         ^^^^^^^^^^^^^^^^^^^ not FFI-safe | note: the lint level is defined here --> $DIR/lint-ctypes-73251-1.rs:2:9 | LL | #![deny(improper_ctypes)] |         ^^^^^^^^^^^^^^^ = note: opaque types have no C equivalent error: aborting due to previous error </ins>

<ins> #![feature(type_alias_impl_trait)] #![deny(improper_ctypes)] pub trait TraitA { type Assoc; } impl TraitA for u32 { type Assoc = u32; } pub trait TraitB { type Assoc; } impl<T> TraitB for T where T: TraitA { type Assoc = <T as TraitA>::Assoc; } type AliasA = impl TraitA<Assoc = u32>; type AliasB = impl TraitB<Assoc = AliasA>; fn use_of_a() -> AliasA { 3 } fn use_of_b() -> AliasB { 3 } extern "C" { pub fn lint_me() -> <AliasB as TraitB>::Assoc; //~ ERROR: uses type `impl TraitA` } fn main() {} </ins>

<ins> error: `extern` block uses type `impl TraitA`, which is not FFI-safe --> $DIR/lint-ctypes-73251-2.rs:29:25 | LL |     pub fn lint_me() -> <AliasB as TraitB>::Assoc; |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | note: the lint level is defined here --> $DIR/lint-ctypes-73251-2.rs:2:9 | LL | #![deny(improper_ctypes)] |         ^^^^^^^^^^^^^^^ = note: opaque types have no C equivalent error: aborting due to previous error </ins>

<ins> // check-pass #![feature(type_alias_impl_trait)] #![deny(improper_ctypes)] pub trait Foo { type Assoc; } impl Foo for () { type Assoc = u32; } type Bar = impl Foo<Assoc = u32>; fn assign() -> Bar {} extern "C" { pub fn lint_me() -> <Bar as Foo>::Assoc; } fn main() {} </ins>