Skip to content

Commit

Permalink
Bubble up opaque <eq> opaque operations instead of picking an order
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Sep 11, 2023
1 parent ad4dd75 commit 930affa
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 21 deletions.
33 changes: 20 additions & 13 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,19 +328,26 @@ fn check_opaque_type_well_formed<'tcx>(

// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
// the bounds that the function supplies.
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_args);
ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
.map_err(|err| {
infcx
.err_ctxt()
.report_mismatched_types(
&ObligationCause::misc(definition_span, def_id),
opaque_ty,
definition_ty,
err,
)
.emit()
})?;
let mut obligations = vec![];
infcx
.insert_hidden_type(
OpaqueTypeKey { def_id, args: identity_args },
&ObligationCause::misc(definition_span, def_id),
param_env,
definition_ty,
true,
&mut obligations,
)
.unwrap();
infcx.add_item_bounds_for_hidden_type(
def_id.to_def_id(),
identity_args,
ObligationCause::misc(definition_span, def_id),
param_env,
definition_ty,
&mut obligations,
);
ocx.register_obligations(obligations);

// 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
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_infer/src/infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,25 @@ impl<'tcx> InferCtxt<'tcx> {
return None;
}
}
DefiningAnchor::Bubble => {}
DefiningAnchor::Bubble => {
if let ty::Alias(ty::Opaque, _) = b.kind() {
// In bubble mode we don't know which of the two opaque types is supposed to have the other
// as a hidden type (both, none or either one of them could be in its defining scope).
let predicate = ty::PredicateKind::AliasRelate(
a.into(),
b.into(),
ty::AliasRelationDirection::Equate,
);
let obligation = traits::Obligation::new(
self.tcx,
cause.clone(),
param_env,
predicate,
);
let obligations = vec![obligation];
return Some(Ok(InferOk { value: (), obligations }));
}
}
DefiningAnchor::Error => return None,
};
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
Expand Down
23 changes: 21 additions & 2 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::ProjectionCacheKey;
use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::GenericArgsRef;
Expand Down Expand Up @@ -623,9 +624,27 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
ty::PredicateKind::AliasRelate(..)
if matches!(self.selcx.infcx.defining_use_anchor, DefiningAnchor::Bubble) =>
{
ProcessResult::Unchanged
}
ty::PredicateKind::AliasRelate(a, b, relate) => match relate {
ty::AliasRelationDirection::Equate => match self
.selcx
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::Yes, a, b)
{
Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)),
},
ty::AliasRelationDirection::Subtype => {
bug!("AliasRelate with subtyping is only used for new solver")
}
},
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
DefineOpaqueTypes::No,
Expand Down
23 changes: 21 additions & 2 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_infer::traits::TraitObligation;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::relate::TypeRelation;
Expand Down Expand Up @@ -960,9 +961,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
ty::PredicateKind::AliasRelate(..)
if matches!(self.infcx.defining_use_anchor, DefiningAnchor::Bubble) =>
{
Ok(EvaluatedToAmbig)
}
ty::PredicateKind::AliasRelate(a, b, relate) => match relate {
ty::AliasRelationDirection::Equate => match self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::Yes, a, b)
{
Ok(inf_ok) => self.evaluate_predicates_recursively(
previous_stack,
inf_ok.into_obligations(),
),
Err(_) => Ok(EvaluatedToErr),
},
ty::AliasRelationDirection::Subtype => {
bug!("AliasRelate subtyping is only used for new solver")
}
},
ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.infcx.at(&obligation.cause, obligation.param_env).eq(
Expand Down
15 changes: 12 additions & 3 deletions tests/ui/impl-trait/async_scope_creep.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![feature(type_alias_impl_trait)]
// edition:2021
// check-pass
//[rpit] check-pass
// revisions: tait rpit

struct Pending {}

Expand All @@ -12,15 +13,23 @@ impl AsyncRead for i32 {}

type PendingReader<'a> = impl AsyncRead + 'a;

type OpeningReadFuture<'a> =
impl std::future::Future<Output = Result<PendingReader<'a>, CantOpen>>;
#[cfg(tait)]
type OpeningReadFuture<'a> = impl std::future::Future<Output = Result<PendingReader<'a>, CantOpen>>;

impl Pending {
async fn read(&mut self) -> Result<impl AsyncRead + '_, CantOpen> {
Ok(42)
}

#[cfg(tait)]
fn read_fut(&mut self) -> OpeningReadFuture<'_> {
self.read() //[tait]~ ERROR: cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`
}

#[cfg(rpit)]
fn read_fut(
&mut self,
) -> impl std::future::Future<Output = Result<PendingReader<'_>, CantOpen>> {
self.read()
}
}
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/impl-trait/async_scope_creep.tait.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0284]: type annotations needed: cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`
--> $DIR/async_scope_creep.rs:26:9
|
LL | self.read()
| ^^^^^^^^^^^ cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0284`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! This tries to prove the APIT's bounds in a canonical query,
//! which doesn't know anything about the defining scope of either
//! opaque type and thus makes a random choice as to which opaque type
//! becomes the hidden type of the other. When we leave the canonical
//! query, we attempt to actually check the defining anchor, but now we
//! have a situation where the RPIT gets constrained outside its anchor.
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
// check-pass

#![feature(type_alias_impl_trait)]

type Opaque = impl Sized;

fn get_rpit() -> impl Clone {}

fn query(_: impl FnOnce() -> Opaque) {}

fn test() -> Opaque {
query(get_rpit);
get_rpit()
}

fn main() {}

0 comments on commit 930affa

Please sign in to comment.