Skip to content

Commit 111194c

Browse files
committed
Orphanck: Reject uncovered opaque types
1 parent 5a6e063 commit 111194c

69 files changed

Lines changed: 618 additions & 349 deletions

File tree

Some content is hidden

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

compiler/rustc_hir_analysis/src/coherence/orphan.rs

Lines changed: 138 additions & 85 deletions
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-
)
50-
},
51-
);
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();
57+
}
58+
_ => bug!(),
59+
}
5260
}
5361
}
5462
OrphanCheckErr::NonLocalInputType(_) => {
@@ -291,20 +299,13 @@ pub(crate) fn orphan_check_impl(
291299
Ok(())
292300
}
293301

294-
/// Checks the coherence orphan rules.
295-
///
296-
/// `impl_def_id` should be the `DefId` of a trait impl.
297-
///
298-
/// To pass, either the trait must be local, or else two conditions must be satisfied:
299-
///
300-
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
301-
/// 2. Some local type must appear in `Self`.
302+
/// Checks the coherence orphan rules for trait impl given by `impl_def_id`.
302303
#[instrument(level = "debug", skip(tcx), ret)]
303304
fn orphan_check<'tcx>(
304305
tcx: TyCtxt<'tcx>,
305306
impl_def_id: LocalDefId,
306307
mode: OrphanCheckMode,
307-
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> {
308+
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>> {
308309
// We only accept this routine to be invoked on implementations
309310
// of a trait, not inherent implementations.
310311
let trait_ref = tcx.impl_trait_ref(impl_def_id);
@@ -357,15 +358,17 @@ fn orphan_check<'tcx>(
357358

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

367-
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
368-
uncovered: collector.uncovered_params,
369+
OrphanCheckErr::UncoveredTy(UncoveredTy {
370+
uncovered: collector.uncovered,
371+
in_self_ty,
369372
local_ty,
370373
})
371374
}
@@ -393,14 +396,20 @@ fn emit_orphan_check_error<'tcx>(
393396
tcx: TyCtxt<'tcx>,
394397
trait_ref: ty::TraitRef<'tcx>,
395398
impl_def_id: LocalDefId,
396-
err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>,
399+
err: OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>,
397400
) -> ErrorGuaranteed {
398-
match err {
399-
traits::OrphanCheckErr::NonLocalInputType(tys) => {
400-
let item = tcx.hir_expect_item(impl_def_id);
401-
let impl_ = item.expect_impl();
402-
let of_trait = impl_.of_trait.unwrap();
401+
let item = tcx.hir_expect_item(impl_def_id);
402+
let impl_ = item.expect_impl();
403+
let of_trait = impl_.of_trait.unwrap();
404+
405+
// Given `impl<A, B> C<B> for D<A>`,
406+
let impl_trait_ref_span = |in_self_ty| match in_self_ty {
407+
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
408+
InSelfTy::No => of_trait.trait_ref.path.span, // point at `C<B>`.
409+
};
403410

411+
match err {
412+
OrphanCheckErr::NonLocalInputType(tys) => {
404413
let span = tcx.def_span(impl_def_id);
405414
let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
406415
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () },
@@ -411,16 +420,12 @@ fn emit_orphan_check_error<'tcx>(
411420
});
412421

413422
for &(mut ty, in_self_ty) in &tys {
414-
// Given `impl<A, B> C<B> for D<A>`,
415-
let span = match in_self_ty {
416-
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
417-
InSelfTy::No => of_trait.trait_ref.path.span, // point at `C<B>`.
418-
};
423+
let span = impl_trait_ref_span(in_self_ty);
424+
let is_foreign =
425+
!of_trait.trait_ref.def_id.is_local() && matches!(in_self_ty, InSelfTy::No);
419426

420427
ty = tcx.erase_and_anonymize_regions(ty);
421428

422-
let is_foreign = !trait_ref.def_id.is_local() && matches!(in_self_ty, InSelfTy::No);
423-
424429
match *ty.kind() {
425430
ty::Slice(_) => {
426431
if is_foreign {
@@ -480,73 +485,121 @@ fn emit_orphan_check_error<'tcx>(
480485

481486
diag.emit()
482487
}
483-
traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
488+
OrphanCheckErr::UncoveredTy(UncoveredTy { uncovered, in_self_ty, local_ty }) => {
489+
let span = impl_trait_ref_span(in_self_ty);
490+
484491
let mut guar = None;
485-
for param_def_id in uncovered {
486-
let ident = tcx.item_ident(param_def_id);
487-
let mut diag = tcx.dcx().struct_span_err(ident.span, "");
488-
decorate_uncovered_ty_params_diag(&mut diag, ident.span, ident, local_ty);
492+
for ty in uncovered {
493+
let span = match ty {
494+
UncoveredTyKind::TyParam(ident) => ident.span,
495+
_ => span,
496+
};
497+
let mut diag = tcx.dcx().struct_span_err(span, "");
498+
decorate_uncovered_ty_diag(&mut diag, span, ty, local_ty);
489499
guar.get_or_insert(diag.emit());
490500
}
501+
// This should not fail because we know that `uncovered` was non-empty at the point of
502+
// iteration since it always contains a single `Unknown` if all else fails.
491503
guar.unwrap()
492504
}
493505
}
494506
}
495507

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

504-
let note = "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local";
505538
if let Some(local_ty) = local_ty {
506-
let msg = format!(
507-
"type parameter `{param}` must be covered by another type when it appears before the first local type (`{local_ty}`)"
539+
diag.primary_message(format!("{subject} must be covered by another type when it appears before the first local type (`{local_ty}`)"));
540+
diag.note(format!("{note},\nand no uncovered type parameters or opaque types appear before that first local type"));
541+
diag.note(
542+
"in this case, 'before' refers to the following order: \
543+
`impl<...> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last",
508544
);
509-
diag.primary_message(msg.clone());
510-
diag.span_label(span, msg);
511-
diag.note(format!(
512-
"{note}, and no uncovered type parameters appear before that first local type"
513-
));
514-
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");
515545
} else {
516-
let msg = format!(
517-
"type parameter `{param}` must be used as the type parameter for some local type"
518-
);
519-
diag.primary_message(format!("{msg} (e.g., `MyStruct<{param}>`)"));
520-
diag.span_label(span, msg);
546+
let example = descr.map(|(_, ty)| format!(" (e.g., `MyStruct<{ty}>`)")).unwrap_or_default();
547+
diag.primary_message(format!(
548+
"{subject} must be used as the argument to some local type{example}"
549+
));
521550
diag.note(note);
522551
diag.note(
523-
"only traits defined in the current crate can be implemented for a type parameter",
552+
"only traits defined in the current crate can be implemented for type parameters and opaque types"
524553
);
525554
}
526555
}
527556

528-
struct UncoveredTyParamCollector<'cx, 'tcx> {
557+
struct UncoveredTyCollector<'cx, 'tcx> {
529558
infcx: &'cx InferCtxt<'tcx>,
530-
uncovered_params: FxIndexSet<DefId>,
559+
uncovered: UncoveredTys<'tcx>,
531560
}
532561

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

547591
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
548-
if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
592+
if ct.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_TY_OPAQUE) {
549593
ct.super_visit_with(self)
550594
}
551595
}
552596
}
597+
598+
type UncoveredTys<'tcx> = FxIndexSet<UncoveredTyKind<'tcx>>;
599+
600+
#[derive(PartialEq, Eq, Hash, Debug)]
601+
enum UncoveredTyKind<'tcx> {
602+
TyParam(Ident),
603+
OpaqueTy(Ty<'tcx>),
604+
Unknown,
605+
}

0 commit comments

Comments
 (0)