Skip to content

Commit

Permalink
omg wtf
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors authored and lcnr committed May 6, 2024
1 parent 748e1a2 commit 77f788e
Show file tree
Hide file tree
Showing 22 changed files with 274 additions and 221 deletions.
68 changes: 19 additions & 49 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,12 @@ fn check_opaque_meets_bounds<'tcx>(
};
let param_env = tcx.param_env(defining_use_anchor);

let infcx = tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).build();
let infcx = tcx
.infer_ctxt()
.with_opaque_type_mode(ty::OpaqueTypeMode::Reveal(
tcx.opaque_types_defined_by(defining_use_anchor),
))
.build();
let ocx = ObligationCtxt::new(&infcx);

let args = match *origin {
Expand All @@ -358,42 +363,23 @@ fn check_opaque_meets_bounds<'tcx>(
}),
};

let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);

// `ReErased` regions appear in the "parent_args" of closures/coroutines.
// We're ignoring them here and replacing them with fresh region variables.
// See tests in ui/type-alias-impl-trait/closure_{parent_args,wf_outlives}.rs.
//
// FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it
// here rather than using ReErased.
let hidden_ty = tcx.type_of(def_id.to_def_id()).instantiate(tcx, args);
let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() {
ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)),
_ => re,
});

let misc_cause = traits::ObligationCause::misc(span, def_id);

match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) {
Ok(()) => {}
Err(ty_err) => {
// Some types may be left "stranded" if they can't be reached
// from a lowered rustc_middle bound but they're mentioned in the HIR.
// This will happen, e.g., when a nested opaque is inside of a non-
// existent associated type, like `impl Trait<Missing = impl Trait>`.
// See <tests/ui/impl-trait/stranded-opaque.rs>.
let ty_err = ty_err.to_string(tcx);
let guar = tcx.dcx().span_delayed_bug(
span,
format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
);
return Err(guar);
}
}
let predicates: Vec<_> = tcx.item_bounds(def_id).iter_instantiated(tcx, args).collect();
let predicates = ocx.normalize(&misc_cause, param_env, predicates);
ocx.register_obligations(
predicates
.into_iter()
.map(|clause| Obligation::new(tcx, misc_cause.clone(), param_env, clause)),
);

// Additionally require the hidden type to be well-formed with only the generics of the opaque type.
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
// hidden type is well formed even without those bounds.
let hidden_ty =
tcx.fold_regions(tcx.type_of(def_id).instantiate(tcx, args), |re, _| match *re {
ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)),
_ => re,
});
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into())));
ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate));
Expand All @@ -411,23 +397,7 @@ fn check_opaque_meets_bounds<'tcx>(
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;

if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
// HACK: this should also fall through to the hidden type check below, but the original
// implementation had a bug where equivalent lifetimes are not identical. This caused us
// to reject existing stable code that is otherwise completely fine. The real fix is to
// compare the hidden types via our type equivalence/relation infra instead of doing an
// identity check.
let _ = infcx.take_opaque_types();
Ok(())
} else {
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
for (mut key, mut ty) in infcx.take_opaque_types() {
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
key = infcx.resolve_vars_if_possible(key);
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
}
Ok(())
}
Ok(())
}

fn sanity_check_found_hidden_type<'tcx>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn fork_with_intercrate(&self, intercrate: bool) -> Self {
Self {
tcx: self.tcx,
defining_opaque_types: self.defining_opaque_types,
opaque_type_mode: self.opaque_type_mode,
considering_regions: self.considering_regions,
skip_leak_check: self.skip_leak_check,
inner: self.inner.clone(),
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl<'tcx> InferCtxt<'tcx> {
},
);

param_env.defining_opaque_types = self.defining_opaque_types;
param_env.opaque_type_mode = self.opaque_type_mode;

Canonicalizer::canonicalize_with_base(
param_env,
Expand Down Expand Up @@ -543,7 +543,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
value: (),
defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(),
opaque_type_mode: infcx.map(|i| i.opaque_type_mode).unwrap_or_default(),
};
Canonicalizer::canonicalize_with_base(
base,
Expand Down Expand Up @@ -613,14 +613,12 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
.max()
.unwrap_or(ty::UniverseIndex::ROOT);

assert!(
!infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types)
);
assert!(!infcx.is_some_and(|infcx| infcx.opaque_type_mode != base.opaque_type_mode));
Canonical {
max_universe,
variables: canonical_variables,
value: (base.value, out_value),
defining_opaque_types: base.defining_opaque_types,
opaque_type_mode: base.opaque_type_mode,
}
}

Expand Down
65 changes: 44 additions & 21 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub use relate::combine::CombineFields;
pub use relate::combine::ObligationEmittingRelation;
pub use relate::StructurallyRelateAliases;
pub use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::traits::Reveal;
use rustc_middle::traits::TreatOpaque;
pub use rustc_middle::ty::IntVarValue;
pub use BoundRegionConversionTime::*;
pub use RegionVariableOrigin::*;
Expand Down Expand Up @@ -245,7 +247,7 @@ pub struct InferCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,

/// The `DefIds` of the opaque types that may have their hidden types constrained.
defining_opaque_types: &'tcx ty::List<LocalDefId>,
opaque_type_mode: ty::OpaqueTypeMode<TyCtxt<'tcx>>,

/// Whether this inference context should care about region obligations in
/// the root universe. Most notably, this is used during hir typeck as region
Expand Down Expand Up @@ -393,8 +395,8 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
self.probe_const_var(vid).ok()
}

fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
self.defining_opaque_types
fn opaque_type_mode(&self) -> ty::OpaqueTypeMode<TyCtxt<'tcx>> {
self.opaque_type_mode
}
}

Expand Down Expand Up @@ -610,7 +612,7 @@ impl fmt::Display for FixupError {
/// Used to configure inference contexts before their creation.
pub struct InferCtxtBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
defining_opaque_types: &'tcx ty::List<LocalDefId>,
opaque_type_mode: ty::OpaqueTypeMode<TyCtxt<'tcx>>,
considering_regions: bool,
skip_leak_check: bool,
/// Whether we are in coherence mode.
Expand All @@ -625,7 +627,7 @@ impl<'tcx> TyCtxt<'tcx> {
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
InferCtxtBuilder {
tcx: self,
defining_opaque_types: ty::List::empty(),
opaque_type_mode: Default::default(),
considering_regions: true,
skip_leak_check: false,
intercrate: false,
Expand All @@ -635,22 +637,23 @@ impl<'tcx> TyCtxt<'tcx> {
}

impl<'tcx> InferCtxtBuilder<'tcx> {
pub fn with_opaque_type_mode(
mut self,
opaque_type_mode: ty::OpaqueTypeMode<TyCtxt<'tcx>>,
) -> Self {
self.opaque_type_mode = opaque_type_mode;
self
}

/// Whenever the `InferCtxt` should be able to handle defining uses of opaque types,
/// you need to call this function. Otherwise the opaque type will be treated opaquely.
///
/// It is only meant to be called in two places, for typeck
/// (via `Inherited::build`) and for the inference context used
/// in mir borrowck.
pub fn with_opaque_type_inference(mut self, defining_anchor: LocalDefId) -> Self {
self.defining_opaque_types = self.tcx.opaque_types_defined_by(defining_anchor);
self
}

pub fn with_defining_opaque_types(
mut self,
defining_opaque_types: &'tcx ty::List<LocalDefId>,
) -> Self {
self.defining_opaque_types = defining_opaque_types;
let types = self.tcx.opaque_types_defined_by(defining_anchor);
self.opaque_type_mode = ty::OpaqueTypeMode::Define(types);
self
}

Expand Down Expand Up @@ -689,23 +692,23 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let infcx = self.with_defining_opaque_types(canonical.defining_opaque_types).build();
let infcx = self.with_opaque_type_mode(canonical.opaque_type_mode).build();
let (value, args) = infcx.instantiate_canonical(span, canonical);
(infcx, value, args)
}

pub fn build(&mut self) -> InferCtxt<'tcx> {
let InferCtxtBuilder {
tcx,
defining_opaque_types,
opaque_type_mode: defining_opaque_types,
considering_regions,
skip_leak_check,
intercrate,
next_trait_solver,
} = *self;
InferCtxt {
tcx,
defining_opaque_types,
opaque_type_mode: defining_opaque_types,
considering_regions,
skip_leak_check,
inner: RefCell::new(InferCtxtInner::new()),
Expand Down Expand Up @@ -1234,10 +1237,30 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow().opaque_type_storage.opaque_types.clone()
}

#[inline(always)]
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
let Some(id) = id.into().as_local() else { return false };
self.defining_opaque_types.contains(&id)
pub fn treat_opaque_ty(&self, reveal: Reveal, def_id: DefId) -> TreatOpaque {
if self.intercrate {
return TreatOpaque::Ambiguous;
}

match reveal {
Reveal::All => return TreatOpaque::Reveal,
Reveal::UserFacing => {}
}

match self.opaque_type_mode {
ty::OpaqueTypeMode::Define(list) => {
if def_id.as_local().is_some_and(|def_id| list.contains(&def_id)) {
return TreatOpaque::Define;
}
}
ty::OpaqueTypeMode::Reveal(list) => {
if def_id.as_local().is_some_and(|def_id| list.contains(&def_id)) {
return TreatOpaque::Reveal;
}
}
}

TreatOpaque::Rigid
}

pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
Expand Down
39 changes: 22 additions & 17 deletions compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use hir::def_id::{DefId, LocalDefId};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::{ObligationCause, TreatOpaque};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::GenericArgKind;
Expand Down Expand Up @@ -59,7 +59,10 @@ impl<'tcx> InferCtxt<'tcx> {
ct_op: |ct| ct,
ty_op: |ty| match *ty.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })
if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() =>
if matches!(
self.treat_opaque_ty(param_env.reveal(), def_id),
TreatOpaque::Define
) && !ty.has_escaping_bound_vars() =>
{
let def_span = self.tcx.def_span(def_id);
let span = if span.contains(def_span) { def_span } else { span };
Expand All @@ -86,16 +89,6 @@ impl<'tcx> InferCtxt<'tcx> {
) -> InferResult<'tcx, ()> {
let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => {
let def_id = def_id.expect_local();
if self.intercrate {
// See comment on `insert_hidden_type` for why this is sufficient in coherence
return Some(self.register_hidden_type(
OpaqueTypeKey { def_id, args },
cause.clone(),
param_env,
b,
));
}
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
// value we are inferring. At present, this is
Expand Down Expand Up @@ -130,8 +123,18 @@ impl<'tcx> InferCtxt<'tcx> {
// let x = || foo(); // returns the Opaque assoc with `foo`
// }
// ```
if !self.can_define_opaque_ty(def_id) {
return None;
match self.treat_opaque_ty(param_env.reveal(), def_id) {
TreatOpaque::Define => {}
TreatOpaque::Reveal | TreatOpaque::Rigid => return None,
TreatOpaque::Ambiguous => {
// See comment on `insert_hidden_type` for why this is sufficient in coherence
return Some(self.register_hidden_type(
OpaqueTypeKey { def_id: def_id.expect_local(), args },
cause.clone(),
param_env,
b,
));
}
}

if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
Expand All @@ -140,8 +143,10 @@ impl<'tcx> InferCtxt<'tcx> {
// no one encounters it in practice.
// It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
// where it is of no concern, so we only check for TAITs.
if self.can_define_opaque_ty(b_def_id)
&& self.tcx.is_type_alias_impl_trait(b_def_id)
if matches!(
self.treat_opaque_ty(param_env.reveal(), b_def_id),
TreatOpaque::Define
) && self.tcx.is_type_alias_impl_trait(b_def_id)
{
self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag {
span: cause.span,
Expand All @@ -151,7 +156,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
Some(self.register_hidden_type(
OpaqueTypeKey { def_id, args },
OpaqueTypeKey { def_id: def_id.expect_local(), args },
cause.clone(),
param_env,
b,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> {
max_universe: ty::UniverseIndex::ROOT,
variables: List::empty(),
value: key,
defining_opaque_types: ty::List::empty(),
opaque_type_mode: Default::default(),
};
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ macro_rules! define_callbacks {
// Increase this limit if necessary, but do try to keep the size low if possible
#[cfg(target_pointer_width = "64")]
const _: () = {
if mem::size_of::<Key<'static>>() > 72 {
if mem::size_of::<Key<'static>>() > 84 {
panic!("{}", concat!(
"the query `",
stringify!($name),
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ pub enum Reveal {
All,
}

pub enum TreatOpaque {
Reveal,
Define,
Rigid,
Ambiguous,
}

/// The reason why we incurred this obligation; used for error reporting.
///
/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the
Expand Down
Loading

0 comments on commit 77f788e

Please sign in to comment.