Skip to content

Commit 1da5ea9

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

File tree

68 files changed

+620
-336
lines changed

Some content is hidden

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

68 files changed

+620
-336
lines changed

compiler/rustc_hir_analysis/src/coherence/orphan.rs

Lines changed: 129 additions & 82 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,24 +29,28 @@ 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-
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+
// Given `impl<A, B> C<B> for D<A>`,
38+
let span = match in_self_ty {
39+
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
40+
InSelfTy::No => hir_trait_ref.path.span, // point at `C<B>`.
41+
};
42+
43+
for ty in uncovered {
44+
let span = match ty {
45+
UncoveredTyKind::TyParam(ident) => ident.span,
46+
UncoveredTyKind::Unknown => span,
47+
_ => bug!(),
48+
};
3949
tcx.node_span_lint(
4050
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+
item.hir_id(),
52+
span,
53+
|diag| decorate_uncovered_ty_diag(diag, span, ty, local_ty),
5154
);
5255
}
5356
}
@@ -295,20 +298,13 @@ pub(crate) fn orphan_check_impl(
295298
Ok(())
296299
}
297300

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`.
301+
/// Checks the coherence orphan rules for trait impl given by `impl_def_id`.
306302
#[instrument(level = "debug", skip(tcx), ret)]
307303
fn orphan_check<'tcx>(
308304
tcx: TyCtxt<'tcx>,
309305
impl_def_id: LocalDefId,
310306
mode: OrphanCheckMode,
311-
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> {
307+
) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>> {
312308
// We only accept this routine to be invoked on implementations
313309
// of a trait, not inherent implementations.
314310
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@@ -361,15 +357,17 @@ fn orphan_check<'tcx>(
361357

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

371-
OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
372-
uncovered: collector.uncovered_params,
368+
OrphanCheckErr::UncoveredTy(UncoveredTy {
369+
uncovered: collector.uncovered,
370+
in_self_ty,
373371
local_ty,
374372
})
375373
}
@@ -397,14 +395,20 @@ fn emit_orphan_check_error<'tcx>(
397395
tcx: TyCtxt<'tcx>,
398396
trait_ref: ty::TraitRef<'tcx>,
399397
impl_def_id: LocalDefId,
400-
err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>,
398+
err: OrphanCheckErr<TyCtxt<'tcx>, UncoveredTys<'tcx>>,
401399
) -> 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();
400+
let item = tcx.hir_expect_item(impl_def_id);
401+
let impl_ = item.expect_impl();
402+
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
403+
404+
// Given `impl<A, B> C<B> for D<A>`,
405+
let impl_trait_ref_span = |in_self_ty| match in_self_ty {
406+
InSelfTy::Yes => impl_.self_ty.span, // point at `D<A>`.
407+
InSelfTy::No => hir_trait_ref.path.span, // point at `C<B>`.
408+
};
407409

410+
match err {
411+
OrphanCheckErr::NonLocalInputType(tys) => {
408412
let span = tcx.def_span(impl_def_id);
409413
let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
410414
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () },
@@ -415,16 +419,11 @@ fn emit_orphan_check_error<'tcx>(
415419
});
416420

417421
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-
};
422+
let span = impl_trait_ref_span(in_self_ty);
423+
let is_foreign = !trait_ref.def_id.is_local() && matches!(in_self_ty, InSelfTy::No);
423424

424425
ty = tcx.erase_regions(ty);
425426

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

485484
diag.emit()
486485
}
487-
traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
486+
OrphanCheckErr::UncoveredTy(UncoveredTy { uncovered, in_self_ty, local_ty }) => {
487+
let span = impl_trait_ref_span(in_self_ty);
488+
488489
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);
490+
for ty in uncovered {
491+
let span = match ty {
492+
UncoveredTyKind::TyParam(ident) => ident.span,
493+
_ => span,
494+
};
495+
let mut diag = tcx.dcx().struct_span_err(span, "");
496+
decorate_uncovered_ty_diag(&mut diag, span, ty, local_ty);
493497
guar.get_or_insert(diag.emit());
494498
}
499+
// This should not fail because we know that `uncovered` was non-empty at the point of
500+
// iteration since it always contains a single `Unknown` if all else fails.
495501
guar.unwrap()
496502
}
497503
}
498504
}
499505

500-
fn decorate_uncovered_ty_params_diag(
506+
fn decorate_uncovered_ty_diag(
501507
diag: &mut Diag<'_, impl EmissionGuarantee>,
502508
span: Span,
503-
param: Ident,
509+
kind: UncoveredTyKind<'_>,
504510
local_ty: Option<Ty<'_>>,
505511
) {
512+
let descr = match kind {
513+
UncoveredTyKind::TyParam(ident) => Some(("type parameter", ident.to_string())),
514+
UncoveredTyKind::OpaqueTy(ty) => Some(("opaque type", ty.to_string())),
515+
UncoveredTyKind::Unknown => None,
516+
};
517+
506518
diag.code(rustc_errors::E0210);
519+
diag.span_label(
520+
span,
521+
match descr {
522+
Some((kind, _)) => format!("uncovered {kind}"),
523+
None => "contains an uncovered type".into(),
524+
},
525+
);
526+
527+
let subject = match &descr {
528+
Some((kind, ty)) => format!("{kind} `{ty}`"),
529+
None => "type parameters and opaque types".into(),
530+
};
531+
532+
let note = "\
533+
implementing a foreign trait is only possible if \
534+
at least one of the types for which it is implemented is local";
507535

508-
let note = "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local";
509536
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}`)"
537+
diag.primary_message(format!("{subject} must be covered by another type when it appears before the first local type (`{local_ty}`)"));
538+
diag.note(format!("{note},\nand no uncovered type parameters or opaque types appear before that first local type"));
539+
diag.note(
540+
"in this case, 'before' refers to the following order: \
541+
`impl<...> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last",
512542
);
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");
519543
} 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);
544+
let example = descr.map(|(_, ty)| format!(" (e.g., `MyStruct<{ty}>`)")).unwrap_or_default();
545+
diag.primary_message(format!(
546+
"{subject} must be used as the argument to some local type{example}"
547+
));
525548
diag.note(note);
526549
diag.note(
527-
"only traits defined in the current crate can be implemented for a type parameter",
550+
"only traits defined in the current crate can be implemented for type parameters and opaque types"
528551
);
529552
}
530553
}
531554

532-
struct UncoveredTyParamCollector<'cx, 'tcx> {
555+
struct UncoveredTyCollector<'cx, 'tcx> {
533556
infcx: &'cx InferCtxt<'tcx>,
534-
uncovered_params: FxIndexSet<DefId>,
557+
uncovered: UncoveredTys<'tcx>,
535558
}
536559

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

551589
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
552-
if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
590+
if ct.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_TY_OPAQUE) {
553591
ct.super_visit_with(self)
554592
}
555593
}
556594
}
595+
596+
type UncoveredTys<'tcx> = FxIndexSet<UncoveredTyKind<'tcx>>;
597+
598+
#[derive(PartialEq, Eq, Hash, Debug)]
599+
enum UncoveredTyKind<'tcx> {
600+
TyParam(Ident),
601+
OpaqueTy(Ty<'tcx>),
602+
Unknown,
603+
}

0 commit comments

Comments
 (0)