Skip to content

Commit 414a046

Browse files
committed
Orphanck: Reject uncovered opaque types
1 parent f2c16e3 commit 414a046

File tree

67 files changed

+605
-338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+605
-338
lines changed

compiler/rustc_hir_analysis/src/coherence/orphan.rs

+136-84
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed};
66
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
77
use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION;
88
use rustc_middle::ty::{
9-
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
9+
self, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
10+
TypingMode,
1011
};
1112
use rustc_middle::{bug, span_bug};
12-
use rustc_span::def_id::{DefId, LocalDefId};
13+
use rustc_span::def_id::LocalDefId;
1314
use rustc_span::{Ident, Span};
14-
use rustc_trait_selection::traits::{
15-
self, InSelfTy, OrphanCheckErr, OrphanCheckMode, UncoveredTyParams,
16-
};
15+
use rustc_trait_selection::traits::{self, InSelfTy, OrphanCheckErr, OrphanCheckMode, UncoveredTy};
1716
use tracing::{debug, instrument};
1817

1918
use crate::errors;
@@ -30,25 +29,34 @@ pub(crate) fn orphan_check_impl(
3029
Ok(()) => {}
3130
Err(err) => match orphan_check(tcx, impl_def_id, OrphanCheckMode::Compat) {
3231
Ok(()) => match err {
33-
OrphanCheckErr::UncoveredTyParams(uncovered_ty_params) => {
34-
let hir_id = tcx.local_def_id_to_hir_id(impl_def_id);
35-
36-
for param_def_id in uncovered_ty_params.uncovered {
37-
let ident = tcx.item_ident(param_def_id);
38-
39-
tcx.node_span_lint(
40-
UNCOVERED_PARAM_IN_PROJECTION,
41-
hir_id,
42-
ident.span,
43-
|diag| {
44-
decorate_uncovered_ty_params_diag(
45-
diag,
46-
ident.span,
47-
ident,
48-
uncovered_ty_params.local_ty,
49-
)
32+
OrphanCheckErr::UncoveredTy(UncoveredTy { uncovered, local_ty, in_self_ty }) => {
33+
let item = tcx.hir_expect_item(impl_def_id);
34+
let impl_ = item.expect_impl();
35+
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
36+
37+
// FIXME: Dedupe!
38+
// Given `impl<A, B> C<B> for D<A>`,
39+
let span = match in_self_ty {
40+
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
41+
InSelfTy::No => hir_trait_ref.path.span, // point at `C<B>`.
42+
};
43+
44+
for ty in uncovered {
45+
match ty {
46+
UncoveredTyKind::TyParam(ident) => tcx.node_span_lint(
47+
UNCOVERED_PARAM_IN_PROJECTION,
48+
item.hir_id(),
49+
ident.span,
50+
|diag| decorate_uncovered_ty_diag(diag, ident.span, ty, local_ty),
51+
),
52+
// FIXME(fmease): This one is hard to explain ^^'
53+
UncoveredTyKind::Unknown => {
54+
let mut diag = tcx.dcx().struct_span_err(span, "");
55+
decorate_uncovered_ty_diag(&mut diag, span, ty, local_ty);
56+
diag.emit();
5057
},
51-
);
58+
_ => bug!(),
59+
}
5260
}
5361
}
5462
OrphanCheckErr::NonLocalInputType(_) => {
@@ -295,20 +303,13 @@ pub(crate) fn orphan_check_impl(
295303
Ok(())
296304
}
297305

298-
/// Checks the coherence orphan rules.
299-
///
300-
/// `impl_def_id` should be the `DefId` of a trait impl.
301-
///
302-
/// To pass, either the trait must be local, or else two conditions must be satisfied:
303-
///
304-
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
305-
/// 2. Some local type must appear in `Self`.
306+
/// Checks the coherence orphan rules for trait impl given by `impl_def_id`.
306307
#[instrument(level = "debug", skip(tcx), ret)]
307308
fn orphan_check<'tcx>(
308309
tcx: TyCtxt<'tcx>,
309310
impl_def_id: LocalDefId,
310311
mode: OrphanCheckMode,
311-
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> {
312+
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>> {
312313
// We only accept this routine to be invoked on implementations
313314
// of a trait, not inherent implementations.
314315
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@@ -361,15 +362,17 @@ fn orphan_check<'tcx>(
361362

362363
// (2) Try to map the remaining inference vars back to generic params.
363364
result.map_err(|err| match err {
364-
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
365+
OrphanCheckErr::UncoveredTy(UncoveredTy { uncovered, in_self_ty, local_ty }) => {
365366
let mut collector =
366-
UncoveredTyParamCollector { infcx: &infcx, uncovered_params: Default::default() };
367+
UncoveredTyCollector { infcx: &infcx, uncovered: Default::default() };
367368
uncovered.visit_with(&mut collector);
368-
// FIXME(fmease): This is very likely reachable.
369-
debug_assert!(!collector.uncovered_params.is_empty());
369+
if collector.uncovered.is_empty() {
370+
collector.uncovered.insert(UncoveredTyKind::Unknown);
371+
}
370372

371-
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
372-
uncovered: collector.uncovered_params,
373+
OrphanCheckErr::UncoveredTy(UncoveredTy {
374+
uncovered: collector.uncovered,
375+
in_self_ty,
373376
local_ty,
374377
})
375378
}
@@ -397,14 +400,20 @@ fn emit_orphan_check_error<'tcx>(
397400
tcx: TyCtxt<'tcx>,
398401
trait_ref: ty::TraitRef<'tcx>,
399402
impl_def_id: LocalDefId,
400-
err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>,
403+
err: OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>,
401404
) -> ErrorGuaranteed {
402-
match err {
403-
traits::OrphanCheckErr::NonLocalInputType(tys) => {
404-
let item = tcx.hir_expect_item(impl_def_id);
405-
let impl_ = item.expect_impl();
406-
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
405+
let item = tcx.hir_expect_item(impl_def_id);
406+
let impl_ = item.expect_impl();
407+
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
408+
409+
// Given `impl<A, B> C<B> for D<A>`,
410+
let impl_trait_ref_span = |in_self_ty| match in_self_ty {
411+
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
412+
InSelfTy::No => hir_trait_ref.path.span, // point at `C<B>`.
413+
};
407414

415+
match err {
416+
OrphanCheckErr::NonLocalInputType(tys) => {
408417
let span = tcx.def_span(impl_def_id);
409418
let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
410419
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () },
@@ -415,16 +424,11 @@ fn emit_orphan_check_error<'tcx>(
415424
});
416425

417426
for &(mut ty, in_self_ty) in &tys {
418-
// Given `impl<A, B> C<B> for D<A>`,
419-
let span = match in_self_ty {
420-
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
421-
InSelfTy::No => hir_trait_ref.path.span, // point at `C<B>`.
422-
};
427+
let span = impl_trait_ref_span(in_self_ty);
428+
let is_foreign = !trait_ref.def_id.is_local() && matches!(in_self_ty, InSelfTy::No);
423429

424430
ty = tcx.erase_regions(ty);
425431

426-
let is_foreign = !trait_ref.def_id.is_local() && matches!(in_self_ty, InSelfTy::No);
427-
428432
match *ty.kind() {
429433
ty::Slice(_) => {
430434
if is_foreign {
@@ -484,73 +488,121 @@ fn emit_orphan_check_error<'tcx>(
484488

485489
diag.emit()
486490
}
487-
traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
491+
OrphanCheckErr::UncoveredTy(UncoveredTy { uncovered, in_self_ty, local_ty }) => {
492+
let span = impl_trait_ref_span(in_self_ty);
493+
488494
let mut guar = None;
489-
for param_def_id in uncovered {
490-
let ident = tcx.item_ident(param_def_id);
491-
let mut diag = tcx.dcx().struct_span_err(ident.span, "");
492-
decorate_uncovered_ty_params_diag(&mut diag, ident.span, ident, local_ty);
495+
for ty in uncovered {
496+
let span = match ty {
497+
UncoveredTyKind::TyParam(ident) => ident.span,
498+
_ => span,
499+
};
500+
let mut diag = tcx.dcx().struct_span_err(span, "");
501+
decorate_uncovered_ty_diag(&mut diag, span, ty, local_ty);
493502
guar.get_or_insert(diag.emit());
494503
}
504+
// This should not fail because we know that `uncovered` was non-empty at the point of
505+
// iteration since it always contains a single `Unknown` if all else fails.
495506
guar.unwrap()
496507
}
497508
}
498509
}
499510

500-
fn decorate_uncovered_ty_params_diag(
511+
fn decorate_uncovered_ty_diag(
501512
diag: &mut Diag<'_, impl EmissionGuarantee>,
502513
span: Span,
503-
param: Ident,
514+
kind: UncoveredTyKind<'_>,
504515
local_ty: Option<Ty<'_>>,
505516
) {
517+
let descr = match kind {
518+
UncoveredTyKind::TyParam(ident) => Some(("type parameter", ident.to_string())),
519+
UncoveredTyKind::OpaqueTy(ty) => Some(("opaque type", ty.to_string())),
520+
UncoveredTyKind::Unknown => None,
521+
};
522+
506523
diag.code(rustc_errors::E0210);
524+
diag.span_label(
525+
span,
526+
match descr {
527+
Some((kind, _)) => format!("uncovered {kind}"),
528+
None => "contains an uncovered type".into(),
529+
},
530+
);
531+
532+
let subject = match &descr {
533+
Some((kind, ty)) => format!("{kind} `{ty}`"),
534+
None => "type parameters and opaque types".into(),
535+
};
536+
537+
let note = "\
538+
implementing a foreign trait is only possible if \
539+
at least one of the types for which it is implemented is local";
507540

508-
let note = "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local";
509541
if let Some(local_ty) = local_ty {
510-
let msg = format!(
511-
"type parameter `{param}` must be covered by another type when it appears before the first local type (`{local_ty}`)"
542+
diag.primary_message(format!("{subject} must be covered by another type when it appears before the first local type (`{local_ty}`)"));
543+
diag.note(format!("{note},\nand no uncovered type parameters or opaque types appear before that first local type"));
544+
diag.note(
545+
"in this case, 'before' refers to the following order: \
546+
`impl<...> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last",
512547
);
513-
diag.primary_message(msg.clone());
514-
diag.span_label(span, msg);
515-
diag.note(format!(
516-
"{note}, and no uncovered type parameters appear before that first local type"
517-
));
518-
diag.note("in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last");
519548
} else {
520-
let msg = format!(
521-
"type parameter `{param}` must be used as the type parameter for some local type"
522-
);
523-
diag.primary_message(format!("{msg} (e.g., `MyStruct<{param}>`)"));
524-
diag.span_label(span, msg);
549+
let example = descr.map(|(_, ty)| format!(" (e.g., `MyStruct<{ty}>`)")).unwrap_or_default();
550+
diag.primary_message(format!(
551+
"{subject} must be used as the argument to some local type{example}"
552+
));
525553
diag.note(note);
526554
diag.note(
527-
"only traits defined in the current crate can be implemented for a type parameter",
555+
"only traits defined in the current crate can be implemented for type parameters and opaque types"
528556
);
529557
}
530558
}
531559

532-
struct UncoveredTyParamCollector<'cx, 'tcx> {
560+
struct UncoveredTyCollector<'cx, 'tcx> {
533561
infcx: &'cx InferCtxt<'tcx>,
534-
uncovered_params: FxIndexSet<DefId>,
562+
uncovered: UncoveredTys<'tcx>,
535563
}
536564

537-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector<'_, 'tcx> {
565+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyCollector<'_, 'tcx> {
538566
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
539-
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
567+
if !ty.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_TY_OPAQUE) {
540568
return;
541569
}
542-
let ty::Infer(ty::TyVar(vid)) = *ty.kind() else {
543-
return ty.super_visit_with(self);
544-
};
545-
let origin = self.infcx.type_var_origin(vid);
546-
if let Some(def_id) = origin.param_def_id {
547-
self.uncovered_params.insert(def_id);
570+
match *ty.kind() {
571+
ty::Infer(ty::TyVar(vid)) => {
572+
if let Some(def_id) = self.infcx.type_var_origin(vid).param_def_id {
573+
let ident = self.infcx.tcx.item_ident(def_id);
574+
self.uncovered.insert(UncoveredTyKind::TyParam(ident));
575+
}
576+
}
577+
// This only works with the old solver. With the next solver, alias types like opaque
578+
// types structurally normalize to an infer var that is "unresolvable" under coherence.
579+
// Furthermore, the orphan checker returns the unnormalized type in such cases (with
580+
// exception like for `Fundamental<?opaque>`) which would be Weak for TAITs and
581+
// Projection for ATPITs.
582+
// FIXME(fmease): One solution I could see working would be to reintroduce
583+
// "TypeVarOriginKind::OpaqueTy(_)" and to stop OrphanChecker from
584+
// remapping to the unnormalized type at all.
585+
// FIXME(fmease): Should we just let uncovered Opaques take precedence over
586+
// uncovered TyParams *inside* Opaques?
587+
ty::Alias(ty::Opaque, alias) if !alias.has_type_flags(TypeFlags::HAS_TY_INFER) => {
588+
self.uncovered.insert(UncoveredTyKind::OpaqueTy(ty));
589+
}
590+
_ => ty.super_visit_with(self),
548591
}
549592
}
550593

551594
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
552-
if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
595+
if ct.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_TY_OPAQUE) {
553596
ct.super_visit_with(self)
554597
}
555598
}
556599
}
600+
601+
type UncoveredTys<'tcx> = FxIndexSet<UncoveredTyKind<'tcx>>;
602+
603+
#[derive(PartialEq, Eq, Hash, Debug)]
604+
enum UncoveredTyKind<'tcx> {
605+
TyParam(Ident),
606+
OpaqueTy(Ty<'tcx>),
607+
Unknown,
608+
}

0 commit comments

Comments
 (0)