Skip to content

Commit 44e2150

Browse files
committed
Double check that hidden types match the expected hidden type
1 parent c67cb3e commit 44e2150

File tree

8 files changed

+224
-12
lines changed

8 files changed

+224
-12
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ fn check_opaque_type_well_formed<'tcx>(
339339
// version.
340340
let errors = ocx.select_all_or_error();
341341

342-
// This is still required for many(half of the tests in ui/type-alias-impl-trait)
343-
// tests to pass
342+
// This is fishy, but we check it again in `check_opaque_meets_bounds`.
343+
// Remove once we can prepopulate with known hidden types.
344344
let _ = infcx.take_opaque_types();
345345

346346
if errors.is_empty() {

compiler/rustc_const_eval/src/util/compare_types.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,13 @@ pub fn is_subtype<'tcx>(
5656
// With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
5757
// we would get unification errors because we're unable to look into opaque types,
5858
// even if they're constrained in our current function.
59-
//
60-
// It seems very unlikely that this hides any bugs.
61-
let _ = infcx.take_opaque_types();
59+
for (key, ty) in infcx.take_opaque_types() {
60+
span_bug!(
61+
ty.hidden_type.span,
62+
"{}, {}",
63+
tcx.type_of(key.def_id).instantiate(tcx, key.args),
64+
ty.hidden_type.ty
65+
);
66+
}
6267
errors.is_empty()
6368
}

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
1919
use rustc_middle::hir::nested_filter;
2020
use rustc_middle::middle::stability::EvalResult;
2121
use rustc_middle::traits::DefiningAnchor;
22+
use rustc_middle::ty::fold::BottomUpFolder;
2223
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
2324
use rustc_middle::ty::util::{Discr, IntTypeExt};
2425
use rustc_middle::ty::GenericArgKind;
2526
use rustc_middle::ty::{
26-
self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
27+
self, AdtDef, ParamEnv, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
28+
TypeVisitableExt,
2729
};
2830
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
2931
use rustc_span::symbol::sym;
@@ -34,6 +36,7 @@ use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplem
3436
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
3537
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
3638
use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
39+
use rustc_type_ir::fold::TypeFoldable;
3740

3841
use std::ops::ControlFlow;
3942

@@ -437,7 +440,7 @@ fn check_opaque_meets_bounds<'tcx>(
437440
// hidden type is well formed even without those bounds.
438441
let predicate =
439442
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into())));
440-
ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate));
443+
ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate));
441444

442445
// Check that all obligations are satisfied by the implementation's
443446
// version.
@@ -464,11 +467,143 @@ fn check_opaque_meets_bounds<'tcx>(
464467
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
465468
}
466469
}
467-
// Clean up after ourselves
468-
let _ = infcx.take_opaque_types();
470+
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
471+
for (key, mut ty) in infcx.take_opaque_types() {
472+
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
473+
sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
474+
}
469475
Ok(())
470476
}
471477

478+
fn sanity_check_found_hidden_type<'tcx>(
479+
tcx: TyCtxt<'tcx>,
480+
key: ty::OpaqueTypeKey<'tcx>,
481+
mut ty: ty::OpaqueHiddenType<'tcx>,
482+
defining_use_anchor: LocalDefId,
483+
origin: &hir::OpaqueTyOrigin,
484+
) -> Result<(), ErrorGuaranteed> {
485+
if ty.ty.is_ty_var() {
486+
// Nothing was actually constrained.
487+
return Ok(());
488+
}
489+
if let ty::Alias(ty::Opaque, alias) = ty.ty.kind() {
490+
if alias.def_id == key.def_id.to_def_id() && alias.args == key.args {
491+
// Nothing was actually constrained, this is an opaque usage that was
492+
// only discovered to be opaque after inference vars resolved.
493+
return Ok(());
494+
}
495+
}
496+
// Closures frequently end up containing erased lifetimes in their final representation.
497+
// These correspond to lifetime variables that never got resolved, so we patch this up here.
498+
ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
499+
tcx,
500+
ty_op: |t| t,
501+
ct_op: |c| c,
502+
lt_op: |l| match l.kind() {
503+
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
504+
_ => l,
505+
},
506+
});
507+
// Get the hidden type, and in case it is in a nested opaque type, find that opaque type's
508+
// usage in the function signature and use the generic arguments from the usage site.
509+
let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
510+
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
511+
if hidden_ty != ty.ty {
512+
hidden_ty = find_and_apply_rpit_substs(
513+
tcx,
514+
hidden_ty,
515+
defining_use_anchor.to_def_id(),
516+
key.def_id.to_def_id(),
517+
)?;
518+
}
519+
}
520+
// If the hidden types differ, emit a type mismatch diagnostic.
521+
if hidden_ty == ty.ty {
522+
Ok(())
523+
} else {
524+
let span = tcx.def_span(key.def_id);
525+
let other = ty::OpaqueHiddenType { ty: hidden_ty, span };
526+
Err(ty.report_mismatch(&other, key.def_id, tcx).emit())
527+
}
528+
}
529+
530+
fn find_and_apply_rpit_substs<'tcx>(
531+
tcx: TyCtxt<'tcx>,
532+
mut hidden_ty: Ty<'tcx>,
533+
function: DefId,
534+
opaque: DefId,
535+
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
536+
// Find use of the RPIT in the function signature and thus find the right substs to
537+
// convert it into the parameter space of the function signature. This is needed,
538+
// because that's what `type_of` returns, against which we compare later.
539+
let ret = tcx.fn_sig(function).instantiate_identity().output();
540+
struct Visitor<'tcx> {
541+
tcx: TyCtxt<'tcx>,
542+
opaque: DefId,
543+
function: DefId,
544+
seen: FxHashSet<DefId>,
545+
}
546+
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
547+
type BreakTy = GenericArgsRef<'tcx>;
548+
549+
#[instrument(level = "trace", skip(self), ret)]
550+
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
551+
trace!("{:#?}", t.kind());
552+
match t.kind() {
553+
ty::Alias(ty::Opaque, alias) => {
554+
trace!(?alias.def_id);
555+
if alias.def_id == self.opaque {
556+
return ControlFlow::Break(alias.args);
557+
} else if self.seen.insert(alias.def_id) {
558+
for clause in self
559+
.tcx
560+
.explicit_item_bounds(alias.def_id)
561+
.iter_instantiated_copied(self.tcx, alias.args)
562+
{
563+
trace!(?clause);
564+
clause.visit_with(self)?;
565+
}
566+
}
567+
}
568+
ty::Alias(ty::Projection, alias) => {
569+
if self.tcx.is_impl_trait_in_trait(alias.def_id)
570+
&& self.tcx.impl_trait_in_trait_parent_fn(alias.def_id) == self.function
571+
{
572+
// If we're lowering to associated item, install the opaque type which is just
573+
// the `type_of` of the trait's associated item. If we're using the old lowering
574+
// strategy, then just reinterpret the associated type like an opaque :^)
575+
self.tcx
576+
.type_of(alias.def_id)
577+
.instantiate(self.tcx, alias.args)
578+
.visit_with(self)?;
579+
}
580+
}
581+
ty::Alias(ty::Weak, alias) => {
582+
self.tcx
583+
.type_of(alias.def_id)
584+
.instantiate(self.tcx, alias.args)
585+
.visit_with(self)?;
586+
}
587+
_ => (),
588+
}
589+
590+
t.super_visit_with(self)
591+
}
592+
}
593+
if let ControlFlow::Break(args) =
594+
ret.visit_with(&mut Visitor { tcx, function, opaque, seen: Default::default() })
595+
{
596+
trace!(?args);
597+
trace!("expected: {hidden_ty:#?}");
598+
hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
599+
trace!("expected: {hidden_ty:#?}");
600+
} else {
601+
tcx.sess
602+
.delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
603+
}
604+
Ok(hidden_ty)
605+
}
606+
472607
fn is_enum_of_nonnullable_ptr<'tcx>(
473608
tcx: TyCtxt<'tcx>,
474609
adt_def: AdtDef<'tcx>,

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
271271
// assertions against dropping an `InferCtxt` without taking opaques.
272272
// FIXME: Once we remove support for the old impl we can remove this.
273273
if input.anchor != DefiningAnchor::Error {
274+
// This seems ok, but fragile.
274275
let _ = infcx.take_opaque_types();
275276
}
276277

src/tools/tidy/src/ui_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::path::{Path, PathBuf};
1010

1111
const ENTRY_LIMIT: usize = 900;
1212
// FIXME: The following limits should be reduced eventually.
13-
const ISSUES_ENTRY_LIMIT: usize = 1894;
13+
const ISSUES_ENTRY_LIMIT: usize = 1893;
1414
const ROOT_ENTRY_LIMIT: usize = 870;
1515

1616
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! This test checks that we don't lose hidden types
2+
//! for *other* opaque types that we register and use
3+
//! to prove bounds while checking that a hidden type
4+
//! satisfies its opaque type's bounds.
5+
6+
#![feature(trivial_bounds, type_alias_impl_trait)]
7+
#![allow(trivial_bounds)]
8+
9+
mod sus {
10+
use super::*;
11+
pub type Sep = impl Sized + std::fmt::Display;
12+
//~^ ERROR: concrete type differs from previous defining opaque type use
13+
pub fn mk_sep() -> Sep {
14+
String::from("hello")
15+
}
16+
17+
pub trait Proj {
18+
type Assoc;
19+
}
20+
impl Proj for () {
21+
type Assoc = sus::Sep;
22+
}
23+
24+
pub struct Bar<T: Proj> {
25+
pub inner: <T as Proj>::Assoc,
26+
pub _marker: T,
27+
}
28+
impl<T: Proj> Clone for Bar<T> {
29+
fn clone(&self) -> Self {
30+
todo!()
31+
}
32+
}
33+
impl<T: Proj<Assoc = i32> + Copy> Copy for Bar<T> {}
34+
// This allows producing `Tait`s via `From`, even though
35+
// `define_tait` is not actually callable, and thus assumed
36+
// `Bar<()>: Copy` even though it isn't.
37+
pub type Tait = impl Copy + From<Bar<()>> + Into<Bar<()>>;
38+
pub fn define_tait() -> Tait
39+
where
40+
// this proves `Bar<()>: Copy`, but `define_tait` is
41+
// now uncallable
42+
(): Proj<Assoc = i32>,
43+
{
44+
Bar { inner: 1i32, _marker: () }
45+
}
46+
}
47+
48+
fn copy_tait(x: sus::Tait) -> (sus::Tait, sus::Tait) {
49+
(x, x)
50+
}
51+
52+
fn main() {
53+
let bar = sus::Bar { inner: sus::mk_sep(), _marker: () };
54+
let (y, z) = copy_tait(bar.into()); // copy a string
55+
drop(y.into()); // drop one instance
56+
println!("{}", z.into().inner); // print the other
57+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: concrete type differs from previous defining opaque type use
2+
--> $DIR/hidden_type_mismatch.rs:11:20
3+
|
4+
LL | pub type Sep = impl Sized + std::fmt::Display;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, got `String`
6+
|
7+
note: previous use here
8+
--> $DIR/hidden_type_mismatch.rs:37:21
9+
|
10+
LL | pub type Tait = impl Copy + From<Bar<()>> + Into<Bar<()>>;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+

tests/ui/issues/issue-83190.rs renamed to tests/ui/type-alias-impl-trait/nested-rpit-with-lifetimes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// check-pass
2-
31
// Regression test for issue #83190, triggering an ICE in borrowck.
42

3+
// check-pass
4+
55
pub trait Any {}
66
impl<T> Any for T {}
77

0 commit comments

Comments
 (0)