Auto merge of #145993 - lcnr:allow-calling-opaques, r=BoxyUwU
`-Znext-solver` allow `ExprKind::Call` for not-yet defined opaques Based on https://github.com/rust-lang/rust/pull/146329. Revival of rust-lang/rust#140496. See the comment on `OpaqueTypesJank`. I've used the following document while working on this https://hackmd.io/Js61f8PRTcyaiyqS-fH9iQ. Fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/181. It does introduce one subtle footgun we may want to handle before stabilization, opened https://github.com/rust-lang/trait-system-refactor-initiative/issues/230 for that. Also cc https://github.com/rust-lang/trait-system-refactor-initiative/issues/231 for deref and index operations r? `@BoxyUwU`
This commit is contained in:
commit
4cd91ef822
41 changed files with 965 additions and 162 deletions
|
|
@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
if self.state.cur_ty.is_ty_var() {
|
||||
// We want to support method and function calls for `impl Deref<Target = ..>`.
|
||||
//
|
||||
// To do so we don't eagerly bail if the current type is the hidden type of an
|
||||
// opaque type and instead return `None` in `fn overloaded_deref_ty` if the
|
||||
// opaque does not have a `Deref` item-bound.
|
||||
if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind()
|
||||
&& !self.infcx.has_opaques_with_sub_unified_hidden_type(vid)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
|||
self.param_env,
|
||||
ty::Binder::dummy(trait_ref),
|
||||
);
|
||||
if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) {
|
||||
// We detect whether the self type implements `Deref` before trying to
|
||||
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
|
||||
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
|
||||
// but fail for `impl OtherTrait`.
|
||||
if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) {
|
||||
debug!("overloaded_deref_ty: cannot match obligation");
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use tracing::{debug, instrument};
|
|||
use super::method::MethodCallee;
|
||||
use super::method::probe::ProbeScope;
|
||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
use crate::method::TreatNotYetDefinedOpaques;
|
||||
use crate::{errors, fluent_generated};
|
||||
|
||||
/// Checks that it is legal to call methods of the trait corresponding
|
||||
|
|
@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
_ => self.check_expr(callee_expr),
|
||||
};
|
||||
|
||||
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
|
||||
let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty);
|
||||
|
||||
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
|
||||
let mut result = None;
|
||||
|
|
@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
arg_exprs: &'tcx [hir::Expr<'tcx>],
|
||||
autoderef: &Autoderef<'a, 'tcx>,
|
||||
) -> Option<CallStep<'tcx>> {
|
||||
let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty());
|
||||
let adjusted_ty =
|
||||
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty());
|
||||
|
||||
// If the callee is a function pointer or a closure, then we're all set.
|
||||
match *adjusted_ty.kind() {
|
||||
|
|
@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
ty::Infer(ty::TyVar(vid)) => {
|
||||
// If we end up with an inference variable which is not the hidden type of
|
||||
// an opaque, emit an error.
|
||||
if !self.has_opaques_with_sub_unified_hidden_type(vid) {
|
||||
self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
ty::Error(_) => {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
|
||||
});
|
||||
|
||||
// We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty`
|
||||
// is `Box<impl FnOnce()>` we choose `FnOnce` instead of `Fn`.
|
||||
//
|
||||
// We try all the different call traits in order and choose the first
|
||||
// one which may apply. So if we treat opaques as inference variables
|
||||
// `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen.
|
||||
if let Some(ok) = self.lookup_method_for_operator(
|
||||
self.misc(call_expr.span),
|
||||
method_name,
|
||||
trait_def_id,
|
||||
adjusted_ty,
|
||||
opt_input_type,
|
||||
TreatNotYetDefinedOpaques::AsRigid,
|
||||
) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
let mut autoref = None;
|
||||
if borrow {
|
||||
// Check for &self vs &mut self in the method signature. Since this is either
|
||||
// the Fn or FnMut trait, it should be one of those.
|
||||
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
|
||||
let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else {
|
||||
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
|
||||
};
|
||||
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded function call ops.
|
||||
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);
|
||||
let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No);
|
||||
|
||||
autoref = Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
|
||||
|
|
|
|||
|
|
@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty = self.try_structurally_resolve_type(sp, ty);
|
||||
|
||||
if !ty.is_ty_var() {
|
||||
ty
|
||||
} else {
|
||||
let e = self.tainted_by_errors().unwrap_or_else(|| {
|
||||
self.err_ctxt()
|
||||
.emit_inference_failure_err(
|
||||
self.body_id,
|
||||
sp,
|
||||
ty.into(),
|
||||
TypeAnnotationNeeded::E0282,
|
||||
true,
|
||||
)
|
||||
.emit()
|
||||
});
|
||||
let err = Ty::new_error(self.tcx, e);
|
||||
self.demand_suptype(sp, err, ty);
|
||||
err
|
||||
}
|
||||
if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) }
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let guar = self.tainted_by_errors().unwrap_or_else(|| {
|
||||
self.err_ctxt()
|
||||
.emit_inference_failure_err(
|
||||
self.body_id,
|
||||
sp,
|
||||
ty.into(),
|
||||
TypeAnnotationNeeded::E0282,
|
||||
true,
|
||||
)
|
||||
.emit()
|
||||
});
|
||||
let err = Ty::new_error(self.tcx, guar);
|
||||
self.demand_suptype(sp, err, ty);
|
||||
err
|
||||
}
|
||||
|
||||
pub(crate) fn structurally_resolve_const(
|
||||
|
|
|
|||
|
|
@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
)?;
|
||||
Ok(pick)
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`.
|
||||
///
|
||||
/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while
|
||||
/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary
|
||||
/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque
|
||||
/// types as rigid to support `impl Deref<Target = impl FnOnce()>` and
|
||||
/// `Box<impl FnOnce()>`.
|
||||
///
|
||||
/// We only want to treat opaque types as rigid if we need to eagerly choose
|
||||
/// between multiple candidates. We otherwise treat them as ordinary inference
|
||||
/// variable to avoid rejecting otherwise correct code.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum TreatNotYetDefinedOpaques {
|
||||
AsInfer,
|
||||
AsRigid,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// `lookup_method_in_trait` is used for overloaded operators.
|
||||
/// It does a very narrow slice of what the normal probe/confirm path does.
|
||||
/// In particular, it doesn't really do any probing: it simply constructs
|
||||
|
|
@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_rhs_ty: Option<Ty<'tcx>>,
|
||||
treat_opaques: TreatNotYetDefinedOpaques,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
|
||||
|
|
@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
|
||||
// Now we want to know if this can be matched
|
||||
if !self.predicate_may_hold(&obligation) {
|
||||
let matches_trait = match treat_opaques {
|
||||
TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation),
|
||||
TreatNotYetDefinedOpaques::AsRigid => {
|
||||
self.predicate_may_hold_opaque_types_jank(&obligation)
|
||||
}
|
||||
};
|
||||
|
||||
if !matches_trait {
|
||||
debug!("--> Cannot match obligation");
|
||||
// Cannot be matched, no such method resolution is possible.
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir};
|
|||
use super::FnCtxt;
|
||||
use super::method::MethodCallee;
|
||||
use crate::Expectation;
|
||||
use crate::method::TreatNotYetDefinedOpaques;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Checks a `a <op>= b`
|
||||
|
|
@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
},
|
||||
);
|
||||
|
||||
let method =
|
||||
self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
|
||||
// We don't consider any other candidates if this lookup fails
|
||||
// so we can freely treat opaque types as inference variables here
|
||||
// to allow more code to compile.
|
||||
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
|
||||
let method = self.lookup_method_for_operator(
|
||||
cause.clone(),
|
||||
opname,
|
||||
trait_did,
|
||||
lhs_ty,
|
||||
opt_rhs_ty,
|
||||
treat_opaques,
|
||||
);
|
||||
match method {
|
||||
Some(ok) => {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
|
|
|||
|
|
@ -117,21 +117,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||
)
|
||||
}
|
||||
UsageKind::UnconstrainedHiddenType(hidden_type) => {
|
||||
let infer_var = hidden_type
|
||||
.ty
|
||||
.walk()
|
||||
.filter_map(ty::GenericArg::as_term)
|
||||
.find(|term| term.is_infer())
|
||||
.unwrap_or_else(|| hidden_type.ty.into());
|
||||
self.err_ctxt()
|
||||
.emit_inference_failure_err(
|
||||
self.body_id,
|
||||
hidden_type.span,
|
||||
infer_var,
|
||||
TypeAnnotationNeeded::E0282,
|
||||
false,
|
||||
)
|
||||
.emit()
|
||||
if let Some(guar) = self.tainted_by_errors() {
|
||||
guar
|
||||
} else {
|
||||
let infer_var = hidden_type
|
||||
.ty
|
||||
.walk()
|
||||
.filter_map(ty::GenericArg::as_term)
|
||||
.find(|term| term.is_infer())
|
||||
.unwrap_or_else(|| hidden_type.ty.into());
|
||||
self.err_ctxt()
|
||||
.emit_inference_failure_err(
|
||||
self.body_id,
|
||||
hidden_type.span,
|
||||
infer_var,
|
||||
TypeAnnotationNeeded::E0282,
|
||||
false,
|
||||
)
|
||||
.emit()
|
||||
}
|
||||
}
|
||||
UsageKind::HasDefiningUse => continue,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_span::{Span, sym};
|
|||
use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::method::MethodCallee;
|
||||
use crate::method::{MethodCallee, TreatNotYetDefinedOpaques};
|
||||
use crate::{FnCtxt, PlaceOp};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
|
@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return None;
|
||||
};
|
||||
|
||||
self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty)
|
||||
// FIXME(trait-system-refactor-initiative#231): we may want to treat
|
||||
// opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`.
|
||||
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
|
||||
self.lookup_method_for_operator(
|
||||
self.misc(span),
|
||||
imm_op,
|
||||
imm_tr,
|
||||
base_ty,
|
||||
opt_rhs_ty,
|
||||
treat_opaques,
|
||||
)
|
||||
}
|
||||
|
||||
fn try_mutable_overloaded_place_op(
|
||||
|
|
@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return None;
|
||||
};
|
||||
|
||||
self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty)
|
||||
// We have to replace the operator with the mutable variant for the
|
||||
// program to compile, so we don't really have a choice here and want
|
||||
// to just try using `DerefMut` even if its not in the item bounds
|
||||
// of the opaque.
|
||||
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
|
||||
self.lookup_method_for_operator(
|
||||
self.misc(span),
|
||||
mut_op,
|
||||
mut_tr,
|
||||
base_ty,
|
||||
opt_rhs_ty,
|
||||
treat_opaques,
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
|
||||
|
|
|
|||
|
|
@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
|
|||
.map(|(k, h)| (k, h.ty))
|
||||
.collect()
|
||||
}
|
||||
fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec<ty::AliasTy<'tcx>> {
|
||||
self.opaques_with_sub_unified_hidden_type(ty)
|
||||
}
|
||||
|
||||
fn register_hidden_type_in_storage(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
|
||||
}
|
||||
|
||||
pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool {
|
||||
if !self.next_trait_solver() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ty_sub_vid = self.sub_unification_table_root_var(ty_vid);
|
||||
let inner = &mut *self.inner.borrow_mut();
|
||||
let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
|
||||
inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| {
|
||||
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
|
||||
let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
|
||||
if opaque_sub_vid == ty_sub_vid {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Searches for an opaque type key whose hidden type is related to `ty_vid`.
|
||||
///
|
||||
/// This only checks for a subtype relation, it does not require equality.
|
||||
pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec<ty::AliasTy<'tcx>> {
|
||||
// Avoid accidentally allowing more code to compile with the old solver.
|
||||
if !self.next_trait_solver() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let ty_sub_vid = self.sub_unification_table_root_var(ty_vid);
|
||||
let inner = &mut *self.inner.borrow_mut();
|
||||
// This is iffy, can't call `type_variables()` as we're already
|
||||
// borrowing the `opaque_type_storage` here.
|
||||
let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
|
||||
inner
|
||||
.opaque_type_storage
|
||||
.iter_opaque_types()
|
||||
.filter_map(|(key, hidden_ty)| {
|
||||
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
|
||||
let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
|
||||
if opaque_sub_vid == ty_sub_vid {
|
||||
return Some(ty::AliasTy::new_from_args(
|
||||
self.tcx,
|
||||
key.def_id.into(),
|
||||
key.args,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
|
||||
debug_assert!(!self.next_trait_solver());
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate;
|
|||
use crate::solve::inspect::ProbeKind;
|
||||
use crate::solve::{
|
||||
BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
|
||||
MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints,
|
||||
MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult,
|
||||
has_no_inference_or_external_constraints,
|
||||
};
|
||||
|
||||
enum AliasBoundKind {
|
||||
|
|
@ -474,7 +475,7 @@ where
|
|||
//
|
||||
// cc trait-system-refactor-initiative#105
|
||||
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
|
||||
let certainty = Certainty::Maybe(cause);
|
||||
let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood };
|
||||
self.probe_trait_candidate(source)
|
||||
.enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty))
|
||||
}
|
||||
|
|
@ -974,11 +975,21 @@ where
|
|||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
// If the self type is sub unified with any opaque type, we
|
||||
// also look at blanket impls for it.
|
||||
let mut assemble_blanket_impls = false;
|
||||
for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) {
|
||||
assemble_blanket_impls = true;
|
||||
// We only use this hack during HIR typeck.
|
||||
let opaque_types = match self.typing_mode() {
|
||||
TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty),
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => vec![],
|
||||
};
|
||||
|
||||
if opaque_types.is_empty() {
|
||||
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
|
||||
return;
|
||||
}
|
||||
|
||||
for &alias_ty in &opaque_types {
|
||||
debug!("self ty is sub unified with {alias_ty:?}");
|
||||
|
||||
struct ReplaceOpaque<I: Interner> {
|
||||
|
|
@ -1028,10 +1039,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// We also need to consider blanket impls for not-yet-defined opaque types.
|
||||
// If the self type is sub unified with any opaque type, we also look at blanket
|
||||
// impls for it.
|
||||
//
|
||||
// See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example.
|
||||
if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() {
|
||||
if assemble_from.should_assemble_impl_candidates() {
|
||||
let cx = self.cx();
|
||||
cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| {
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
|
|
@ -1062,7 +1074,15 @@ where
|
|||
}
|
||||
|
||||
if candidates.is_empty() {
|
||||
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
|
||||
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
|
||||
let certainty = Certainty::Maybe {
|
||||
cause: MaybeCause::Ambiguity,
|
||||
opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy,
|
||||
};
|
||||
candidates
|
||||
.extend(self.probe_trait_candidate(source).enter(|this| {
|
||||
this.evaluate_added_goals_and_make_canonical_response(certainty)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use rustc_index::IndexVec;
|
|||
use rustc_type_ir::data_structures::HashSet;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::relate::solver_relating::RelateExt;
|
||||
use rustc_type_ir::solve::OpaqueTypesJank;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
|
||||
TypeFoldable,
|
||||
|
|
@ -141,8 +142,10 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) =
|
||||
certainty
|
||||
if let Certainty::Maybe {
|
||||
cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. },
|
||||
opaque_types_jank,
|
||||
} = certainty
|
||||
{
|
||||
// If we have overflow, it's probable that we're substituting a type
|
||||
// into itself infinitely and any partial substitutions in the query
|
||||
|
|
@ -155,7 +158,7 @@ where
|
|||
//
|
||||
// Changing this to retain some constraints in the future
|
||||
// won't be a breaking change, so this is good enough for now.
|
||||
return Ok(self.make_ambiguous_response_no_constraints(cause));
|
||||
return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank));
|
||||
}
|
||||
|
||||
let external_constraints =
|
||||
|
|
@ -199,10 +202,13 @@ where
|
|||
.count();
|
||||
if num_non_region_vars > self.cx().recursion_limit() {
|
||||
debug!(?num_non_region_vars, "too many inference variables -> overflow");
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
|
||||
suggest_increasing_limit: true,
|
||||
keep_constraints: false,
|
||||
}));
|
||||
return Ok(self.make_ambiguous_response_no_constraints(
|
||||
MaybeCause::Overflow {
|
||||
suggest_increasing_limit: true,
|
||||
keep_constraints: false,
|
||||
},
|
||||
OpaqueTypesJank::AllGood,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -216,13 +222,14 @@ where
|
|||
/// ambiguity but return constrained variables to guide inference.
|
||||
pub(in crate::solve) fn make_ambiguous_response_no_constraints(
|
||||
&self,
|
||||
maybe_cause: MaybeCause,
|
||||
cause: MaybeCause,
|
||||
opaque_types_jank: OpaqueTypesJank,
|
||||
) -> CanonicalResponse<I> {
|
||||
response_no_constraints_raw(
|
||||
self.cx(),
|
||||
self.max_input_universe,
|
||||
self.variables,
|
||||
Certainty::Maybe(maybe_cause),
|
||||
Certainty::Maybe { cause, opaque_types_jank },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*;
|
|||
use rustc_type_ir::relate::Relate;
|
||||
use rustc_type_ir::relate::solver_relating::RelateExt;
|
||||
use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind};
|
||||
use rustc_type_ir::solve::OpaqueTypesJank;
|
||||
use rustc_type_ir::{
|
||||
self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
|
|
@ -151,6 +152,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
|
|||
stalled_on: Option<GoalStalledOn<Self::Interner>>,
|
||||
) -> Result<GoalEvaluation<Self::Interner>, NoSolution>;
|
||||
|
||||
/// Checks whether evaluating `goal` may hold while treating not-yet-defined
|
||||
/// opaque types as being kind of rigid.
|
||||
///
|
||||
/// See the comment on [OpaqueTypesJank] for more details.
|
||||
fn root_goal_may_hold_opaque_types_jank(
|
||||
&self,
|
||||
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
|
||||
) -> bool;
|
||||
|
||||
/// Check whether evaluating `goal` with a depth of `root_depth` may
|
||||
/// succeed. This only returns `false` if the goal is guaranteed to
|
||||
/// not hold. In case evaluation overflows and fails with ambiguity this
|
||||
|
|
@ -193,6 +203,24 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn root_goal_may_hold_opaque_types_jank(
|
||||
&self,
|
||||
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
|
||||
) -> bool {
|
||||
self.probe(|| {
|
||||
EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| {
|
||||
ecx.evaluate_goal(GoalSource::Misc, goal, None)
|
||||
})
|
||||
.is_ok_and(|r| match r.certainty {
|
||||
Certainty::Yes => true,
|
||||
Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank {
|
||||
OpaqueTypesJank::AllGood => true,
|
||||
OpaqueTypesJank::ErrorIfRigidSelfTy => false,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn root_goal_may_hold_with_depth(
|
||||
&self,
|
||||
root_depth: usize,
|
||||
|
|
@ -407,8 +435,12 @@ where
|
|||
// If we have run this goal before, and it was stalled, check that any of the goal's
|
||||
// args have changed. Otherwise, we don't need to re-run the goal because it'll remain
|
||||
// stalled, since it'll canonicalize the same way and evaluation is pure.
|
||||
if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) =
|
||||
stalled_on
|
||||
if let Some(GoalStalledOn {
|
||||
num_opaques,
|
||||
ref stalled_vars,
|
||||
ref sub_roots,
|
||||
stalled_certainty,
|
||||
}) = stalled_on
|
||||
&& !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value))
|
||||
&& !sub_roots
|
||||
.iter()
|
||||
|
|
@ -419,7 +451,7 @@ where
|
|||
NestedNormalizationGoals::empty(),
|
||||
GoalEvaluation {
|
||||
goal,
|
||||
certainty: Certainty::Maybe(stalled_cause),
|
||||
certainty: stalled_certainty,
|
||||
has_changed: HasChanged::No,
|
||||
stalled_on,
|
||||
},
|
||||
|
|
@ -468,7 +500,7 @@ where
|
|||
|
||||
let stalled_on = match certainty {
|
||||
Certainty::Yes => None,
|
||||
Certainty::Maybe(stalled_cause) => match has_changed {
|
||||
Certainty::Maybe { .. } => match has_changed {
|
||||
// FIXME: We could recompute a *new* set of stalled variables by walking
|
||||
// through the orig values, resolving, and computing the root vars of anything
|
||||
// that is not resolved. Only when *these* have changed is it meaningful
|
||||
|
|
@ -518,7 +550,7 @@ where
|
|||
.len(),
|
||||
stalled_vars,
|
||||
sub_roots,
|
||||
stalled_cause,
|
||||
stalled_certainty: certainty,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
@ -634,7 +666,7 @@ where
|
|||
if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) {
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
Certainty::Maybe { .. } => {
|
||||
self.nested_goals.push((source, goal, None));
|
||||
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
|
||||
}
|
||||
|
|
@ -710,7 +742,7 @@ where
|
|||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
Certainty::Maybe { .. } => {
|
||||
self.nested_goals.push((source, with_resolved_vars, stalled_on));
|
||||
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
|
||||
}
|
||||
|
|
@ -724,7 +756,7 @@ where
|
|||
|
||||
match certainty {
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
Certainty::Maybe { .. } => {
|
||||
self.nested_goals.push((source, goal, stalled_on));
|
||||
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
|
||||
}
|
||||
|
|
@ -1184,28 +1216,12 @@ where
|
|||
pub(crate) fn opaques_with_sub_unified_hidden_type(
|
||||
&self,
|
||||
self_ty: I::Ty,
|
||||
) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> {
|
||||
let delegate = self.delegate;
|
||||
delegate
|
||||
.clone_opaque_types_lookup_table()
|
||||
.into_iter()
|
||||
.chain(delegate.clone_duplicate_opaque_types())
|
||||
.filter_map(move |(key, hidden_ty)| {
|
||||
if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() {
|
||||
if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() {
|
||||
if delegate.sub_unification_table_root_var(self_vid)
|
||||
== delegate.sub_unification_table_root_var(hidden_vid)
|
||||
{
|
||||
return Some(ty::AliasTy::new_from_args(
|
||||
delegate.cx(),
|
||||
key.def_id.into(),
|
||||
key.args,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
) -> Vec<ty::AliasTy<I>> {
|
||||
if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() {
|
||||
self.delegate.opaques_with_sub_unified_hidden_type(vid)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,9 +158,10 @@ where
|
|||
if self.may_use_unstable_feature(param_env, symbol) {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
|
||||
MaybeCause::Ambiguity,
|
||||
))
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe {
|
||||
cause: MaybeCause::Ambiguity,
|
||||
opaque_types_jank: OpaqueTypesJank::AllGood,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,18 +279,21 @@ where
|
|||
|
||||
fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {
|
||||
debug_assert!(candidates.len() > 1);
|
||||
let maybe_cause =
|
||||
candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| {
|
||||
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
|
||||
let (cause, opaque_types_jank) = candidates.iter().fold(
|
||||
(MaybeCause::Ambiguity, OpaqueTypesJank::AllGood),
|
||||
|(c, jank), candidates| {
|
||||
// We pull down the certainty of `Certainty::Yes` to ambiguity when combining
|
||||
// these responses, b/c we're combining more than one response and this we
|
||||
// don't know which one applies.
|
||||
let candidate = match candidates.result.value.certainty {
|
||||
Certainty::Yes => MaybeCause::Ambiguity,
|
||||
Certainty::Maybe(candidate) => candidate,
|
||||
};
|
||||
maybe_cause.or(candidate)
|
||||
});
|
||||
self.make_ambiguous_response_no_constraints(maybe_cause)
|
||||
match candidates.result.value.certainty {
|
||||
Certainty::Yes => (c, jank),
|
||||
Certainty::Maybe { cause, opaque_types_jank } => {
|
||||
(c.or(cause), jank.or(opaque_types_jank))
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)
|
||||
}
|
||||
|
||||
/// If we fail to merge responses we flounder and return overflow or ambiguity.
|
||||
|
|
@ -427,6 +431,7 @@ pub struct GoalStalledOn<I: Interner> {
|
|||
pub num_opaques: usize,
|
||||
pub stalled_vars: Vec<I::GenericArg>,
|
||||
pub sub_roots: Vec<TyVid>,
|
||||
/// The cause that will be returned on subsequent evaluations if this goal remains stalled.
|
||||
pub stalled_cause: MaybeCause,
|
||||
/// The certainty that will be returned on subsequent evaluations if this
|
||||
/// goal remains stalled.
|
||||
pub stalled_certainty: Certainty,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ where
|
|||
fn is_ambiguous_result(result: QueryResult<I>) -> bool {
|
||||
result.is_ok_and(|response| {
|
||||
has_no_inference_or_external_constraints(response)
|
||||
&& matches!(response.value.certainty, Certainty::Maybe(_))
|
||||
&& matches!(response.value.certainty, Certainty::Maybe { .. })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ where
|
|||
//
|
||||
// Only goals proven via the trait solver should be region dependent.
|
||||
Certainty::Yes => {}
|
||||
Certainty::Maybe(_) => {
|
||||
Certainty::Maybe { .. } => {
|
||||
self.obligations.register(obligation, None);
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +258,7 @@ where
|
|||
infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
|
||||
}
|
||||
}
|
||||
Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
|
||||
Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,15 +95,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
|
|||
root_obligation.cause.span,
|
||||
None,
|
||||
) {
|
||||
Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => {
|
||||
(FulfillmentErrorCode::Ambiguity { overflow: None }, true)
|
||||
}
|
||||
Ok(GoalEvaluation {
|
||||
certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. },
|
||||
..
|
||||
}) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
|
||||
Ok(GoalEvaluation {
|
||||
certainty:
|
||||
Certainty::Maybe(MaybeCause::Overflow {
|
||||
suggest_increasing_limit,
|
||||
keep_constraints: _,
|
||||
}),
|
||||
Certainty::Maybe {
|
||||
cause:
|
||||
MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => (
|
||||
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
|
||||
|
|
@ -266,7 +268,8 @@ impl<'tcx> BestObligation<'tcx> {
|
|||
);
|
||||
// Skip nested goals that aren't the *reason* for our goal's failure.
|
||||
match (self.consider_ambiguities, nested_goal.result()) {
|
||||
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
|
||||
(true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }))
|
||||
| (false, Err(_)) => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
|
|
@ -407,7 +410,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
|
|||
let tcx = goal.infcx().tcx;
|
||||
// Skip goals that aren't the *reason* for our goal's failure.
|
||||
match (self.consider_ambiguities, goal.result()) {
|
||||
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
|
||||
(true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => {
|
||||
}
|
||||
_ => return ControlFlow::Continue(()),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
|||
inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
|
||||
assert_matches!(
|
||||
shallow_certainty.replace(c),
|
||||
None | Some(Certainty::Maybe(MaybeCause::Ambiguity))
|
||||
None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })
|
||||
);
|
||||
}
|
||||
inspect::ProbeStep::NestedProbe(ref probe) => {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select {
|
|||
|
||||
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
|
||||
// codegen, and only on the good path.
|
||||
if matches!(goal.result().unwrap(), Certainty::Maybe(..)) {
|
||||
if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) {
|
||||
return ControlFlow::Break(Ok(None));
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
|
|||
) -> bool {
|
||||
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
|
||||
// codegen, and only on the good path.
|
||||
if matches!(other.result().unwrap(), Certainty::Maybe(..)) {
|
||||
if matches!(other.result().unwrap(), Certainty::Maybe { .. }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -143,13 +143,13 @@ fn to_selection<'tcx>(
|
|||
span: Span,
|
||||
cand: inspect::InspectCandidate<'_, 'tcx>,
|
||||
) -> Option<Selection<'tcx>> {
|
||||
if let Certainty::Maybe(..) = cand.shallow_certainty() {
|
||||
if let Certainty::Maybe { .. } = cand.shallow_certainty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let nested = match cand.result().expect("expected positive result") {
|
||||
Certainty::Yes => thin_vec![],
|
||||
Certainty::Maybe(_) => cand
|
||||
Certainty::Maybe { .. } => cand
|
||||
.instantiate_nested_goals(span)
|
||||
.into_iter()
|
||||
.map(|nested| {
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
|
|||
// was irrelevant.
|
||||
match goal.result() {
|
||||
Ok(Certainty::Yes) | Err(NoSolution) => return,
|
||||
Ok(Certainty::Maybe(_)) => {}
|
||||
Ok(Certainty::Maybe { .. }) => {}
|
||||
}
|
||||
|
||||
// For bound predicates we simply call `infcx.enter_forall`
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use rustc_infer::traits::solve::Goal;
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::infer::canonical::OriginalQueryValues;
|
||||
use crate::solve::SolverDelegate;
|
||||
use crate::traits::{
|
||||
EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext,
|
||||
};
|
||||
|
|
@ -15,6 +18,20 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.evaluate_obligation_no_overflow(obligation).may_apply()
|
||||
}
|
||||
|
||||
/// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank)
|
||||
/// for more details.
|
||||
fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool {
|
||||
if self.next_trait_solver() {
|
||||
<&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new(
|
||||
self.tcx,
|
||||
obligation.param_env,
|
||||
obligation.predicate,
|
||||
))
|
||||
} else {
|
||||
self.predicate_may_hold(obligation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates whether the predicate can be satisfied in the given
|
||||
/// `ParamEnv`, and returns `false` if not certain. However, this is
|
||||
/// not entirely accurate if inference variables are involved.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::fold::TypeFoldable;
|
|||
use crate::inherent::*;
|
||||
use crate::relate::RelateResult;
|
||||
use crate::relate::combine::PredicateEmittingRelation;
|
||||
use crate::{self as ty, Interner};
|
||||
use crate::{self as ty, Interner, TyVid};
|
||||
|
||||
/// The current typing mode of an inference context. We unfortunately have some
|
||||
/// slightly different typing rules depending on the current context. See the
|
||||
|
|
@ -271,6 +271,7 @@ pub trait InferCtxtLike: Sized {
|
|||
&self,
|
||||
prev_entries: Self::OpaqueTypeStorageEntries,
|
||||
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
|
||||
fn opaques_with_sub_unified_hidden_type(&self, ty: TyVid) -> Vec<ty::AliasTy<Self::Interner>>;
|
||||
|
||||
fn register_hidden_type_in_storage(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -269,11 +269,70 @@ impl<I: Interner> NestedNormalizationGoals<I> {
|
|||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
||||
pub enum Certainty {
|
||||
Yes,
|
||||
Maybe(MaybeCause),
|
||||
Maybe { cause: MaybeCause, opaque_types_jank: OpaqueTypesJank },
|
||||
}
|
||||
|
||||
/// Supporting not-yet-defined opaque types in HIR typeck is somewhat
|
||||
/// challenging. Ideally we'd normalize them to a new inference variable
|
||||
/// and just defer type inference which relies on the opaque until we've
|
||||
/// constrained the hidden type.
|
||||
///
|
||||
/// This doesn't work for method and function calls as we need to guide type
|
||||
/// inference for the function arguments. We treat not-yet-defined opaque types
|
||||
/// as if they were rigid instead in these places.
|
||||
///
|
||||
/// When we encounter a `?hidden_type_of_opaque: Trait<?var>` goal, we use the
|
||||
/// item bounds and blanket impls to guide inference by constraining other type
|
||||
/// variables, see `EvalCtxt::try_assemble_bounds_via_registered_opaques`. We
|
||||
/// always keep the certainty as `Maybe` so that we properly prove these goals
|
||||
/// once the hidden type has been constrained.
|
||||
///
|
||||
/// If we fail to prove the trait goal via item bounds or blanket impls, the
|
||||
/// goal would have errored if the opaque type were rigid. In this case, we
|
||||
/// set `OpaqueTypesJank::ErrorIfRigidSelfTy` in the [Certainty].
|
||||
///
|
||||
/// Places in HIR typeck where we want to treat not-yet-defined opaque types as if
|
||||
/// they were kind of rigid then use `fn root_goal_may_hold_opaque_types_jank` which
|
||||
/// returns `false` if the goal doesn't hold or if `OpaqueTypesJank::ErrorIfRigidSelfTy`
|
||||
/// is set (i.e. proving it required relies on some `?hidden_ty: NotInItemBounds` goal).
|
||||
///
|
||||
/// This is subtly different from actually treating not-yet-defined opaque types as
|
||||
/// rigid, e.g. it allows constraining opaque types if they are not the self-type of
|
||||
/// a goal. It is good enough for now and only matters for very rare type inference
|
||||
/// edge cases. We can improve this later on if necessary.
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
||||
pub enum OpaqueTypesJank {
|
||||
AllGood,
|
||||
ErrorIfRigidSelfTy,
|
||||
}
|
||||
impl OpaqueTypesJank {
|
||||
fn and(self, other: OpaqueTypesJank) -> OpaqueTypesJank {
|
||||
match (self, other) {
|
||||
(OpaqueTypesJank::AllGood, OpaqueTypesJank::AllGood) => OpaqueTypesJank::AllGood,
|
||||
(OpaqueTypesJank::ErrorIfRigidSelfTy, _) | (_, OpaqueTypesJank::ErrorIfRigidSelfTy) => {
|
||||
OpaqueTypesJank::ErrorIfRigidSelfTy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or(self, other: OpaqueTypesJank) -> OpaqueTypesJank {
|
||||
match (self, other) {
|
||||
(OpaqueTypesJank::ErrorIfRigidSelfTy, OpaqueTypesJank::ErrorIfRigidSelfTy) => {
|
||||
OpaqueTypesJank::ErrorIfRigidSelfTy
|
||||
}
|
||||
(OpaqueTypesJank::AllGood, _) | (_, OpaqueTypesJank::AllGood) => {
|
||||
OpaqueTypesJank::AllGood
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Certainty {
|
||||
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
|
||||
pub const AMBIGUOUS: Certainty = Certainty::Maybe {
|
||||
cause: MaybeCause::Ambiguity,
|
||||
opaque_types_jank: OpaqueTypesJank::AllGood,
|
||||
};
|
||||
|
||||
/// Use this function to merge the certainty of multiple nested subgoals.
|
||||
///
|
||||
|
|
@ -290,14 +349,23 @@ impl Certainty {
|
|||
pub fn and(self, other: Certainty) -> Certainty {
|
||||
match (self, other) {
|
||||
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
|
||||
(Certainty::Yes, Certainty::Maybe(_)) => other,
|
||||
(Certainty::Maybe(_), Certainty::Yes) => self,
|
||||
(Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)),
|
||||
(Certainty::Yes, Certainty::Maybe { .. }) => other,
|
||||
(Certainty::Maybe { .. }, Certainty::Yes) => self,
|
||||
(
|
||||
Certainty::Maybe { cause: a_cause, opaque_types_jank: a_jank },
|
||||
Certainty::Maybe { cause: b_cause, opaque_types_jank: b_jank },
|
||||
) => Certainty::Maybe {
|
||||
cause: a_cause.and(b_cause),
|
||||
opaque_types_jank: a_jank.and(b_jank),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn overflow(suggest_increasing_limit: bool) -> Certainty {
|
||||
Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false })
|
||||
Certainty::Maybe {
|
||||
cause: MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false },
|
||||
opaque_types_jank: OpaqueTypesJank::AllGood,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
error[E0369]: cannot add `{integer}` to `impl Sized`
|
||||
--> $DIR/ambiguous-ops.rs:17:15
|
||||
|
|
||||
LL | add() + 1
|
||||
| ----- ^ - {integer}
|
||||
| |
|
||||
| impl Sized
|
||||
|
||||
error[E0368]: binary assignment operation `*=` cannot be applied to type `impl Sized`
|
||||
--> $DIR/ambiguous-ops.rs:31:9
|
||||
|
|
||||
LL | temp *= 2;
|
||||
| ----^^^^^
|
||||
| |
|
||||
| cannot use `*=` on type `impl Sized`
|
||||
|
||||
error[E0614]: type `DerefWrapper<impl Sized>` cannot be dereferenced
|
||||
--> $DIR/ambiguous-ops.rs:57:22
|
||||
|
|
||||
LL | let _rarw = &*explicit_deref();
|
||||
| ^^^^^^^^^^^^^^^^^ can't be dereferenced
|
||||
|
||||
error[E0614]: type `DerefWrapper<impl Sized>` cannot be dereferenced
|
||||
--> $DIR/ambiguous-ops.rs:69:9
|
||||
|
|
||||
LL | *explicit_deref_mut() = 1;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ can't be dereferenced
|
||||
|
||||
error[E0277]: the type `impl Sized` cannot be indexed by `_`
|
||||
--> $DIR/ambiguous-ops.rs:94:18
|
||||
|
|
||||
LL | let _y = explicit_index()[0];
|
||||
| ^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_`
|
||||
|
|
||||
= help: the trait `Index<_>` is not implemented for `impl Sized`
|
||||
note: required for `IndexWrapper<impl Sized>` to implement `Index<_>`
|
||||
--> $DIR/ambiguous-ops.rs:81:22
|
||||
|
|
||||
LL | impl<T: Index<U>, U> Index<U> for IndexWrapper<T> {
|
||||
| -------- ^^^^^^^^ ^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
|
||||
error[E0277]: the type `impl Sized` cannot be indexed by `_`
|
||||
--> $DIR/ambiguous-ops.rs:106:9
|
||||
|
|
||||
LL | explicit_index_mut()[0] = 1;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_`
|
||||
|
|
||||
= help: the trait `Index<_>` is not implemented for `impl Sized`
|
||||
note: required for `IndexWrapper<impl Sized>` to implement `Index<_>`
|
||||
--> $DIR/ambiguous-ops.rs:81:22
|
||||
|
|
||||
LL | impl<T: Index<U>, U> Index<U> for IndexWrapper<T> {
|
||||
| -------- ^^^^^^^^ ^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0368, E0369, E0614.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
117
tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs
Normal file
117
tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] check-pass
|
||||
|
||||
// Make sure we support non-call operations for opaque types even if
|
||||
// its not part of its item bounds.
|
||||
|
||||
use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
|
||||
fn mk<T>() -> T {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add() -> impl Sized {
|
||||
let unconstrained = if false {
|
||||
add() + 1
|
||||
//[current]~^ ERROR cannot add `{integer}` to `impl Sized
|
||||
} else {
|
||||
let with_infer = mk();
|
||||
let _ = with_infer + 1;
|
||||
with_infer
|
||||
};
|
||||
let _: u32 = unconstrained;
|
||||
1u32
|
||||
}
|
||||
|
||||
fn mul_assign() -> impl Sized {
|
||||
if false {
|
||||
let mut temp = mul_assign();
|
||||
temp *= 2;
|
||||
//[current]~^ ERROR binary assignment operation `*=` cannot be applied to type `impl Sized`
|
||||
}
|
||||
|
||||
let mut with_infer = mk();
|
||||
with_infer *= 2;
|
||||
let _: u32 = with_infer;
|
||||
|
||||
1u32
|
||||
}
|
||||
|
||||
struct DerefWrapper<T>(T);
|
||||
impl<T: Deref> Deref for DerefWrapper<T> {
|
||||
type Target = T::Target;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
impl<T: DerefMut> DerefMut for DerefWrapper<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn explicit_deref() -> DerefWrapper<impl Sized> {
|
||||
if false {
|
||||
let _rarw = &*explicit_deref();
|
||||
//[current]~^ ERROR type `DerefWrapper<impl Sized>` cannot be dereferenced
|
||||
|
||||
let mut with_infer = DerefWrapper(mk());
|
||||
let _rarw = &*with_infer;
|
||||
with_infer
|
||||
} else {
|
||||
DerefWrapper(&1u32)
|
||||
}
|
||||
}
|
||||
fn explicit_deref_mut() -> DerefWrapper<impl Sized> {
|
||||
if false {
|
||||
*explicit_deref_mut() = 1;
|
||||
//[current]~^ ERROR type `DerefWrapper<impl Sized>` cannot be dereferenced
|
||||
|
||||
let mut with_infer = DerefWrapper(Default::default());
|
||||
*with_infer = 1;
|
||||
with_infer
|
||||
} else {
|
||||
DerefWrapper(Box::new(1u32))
|
||||
}
|
||||
}
|
||||
|
||||
struct IndexWrapper<T>(T);
|
||||
impl<T: Index<U>, U> Index<U> for IndexWrapper<T> {
|
||||
type Output = T::Output;
|
||||
fn index(&self, index: U) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
impl<T: IndexMut<U>, U> IndexMut<U> for IndexWrapper<T> {
|
||||
fn index_mut(&mut self, index: U) -> &mut Self::Output {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
fn explicit_index() -> IndexWrapper<impl Sized> {
|
||||
if false {
|
||||
let _y = explicit_index()[0];
|
||||
//[current]~^ ERROR the type `impl Sized` cannot be indexed by `_`
|
||||
|
||||
let with_infer = IndexWrapper(Default::default());
|
||||
let _y = with_infer[0];
|
||||
with_infer
|
||||
} else {
|
||||
IndexWrapper([1u32])
|
||||
}
|
||||
}
|
||||
fn explicit_index_mut() -> IndexWrapper<impl Sized> {
|
||||
if false {
|
||||
explicit_index_mut()[0] = 1;
|
||||
//[current]~^ ERROR the type `impl Sized` cannot be indexed by `_`
|
||||
|
||||
let mut with_infer = IndexWrapper(Default::default());
|
||||
with_infer[0] = 1;
|
||||
with_infer
|
||||
} else {
|
||||
IndexWrapper([1u32])
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
error[E0382]: use of moved value: `var`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9
|
||||
|
|
||||
LL | let mut var = item_bound_is_too_weak();
|
||||
| ------- move occurs because `var` has type `impl FnOnce()`, which does not implement the `Copy` trait
|
||||
LL | var();
|
||||
| ----- `var` moved due to this call
|
||||
LL | var();
|
||||
| ^^^ value used here after move
|
||||
|
|
||||
note: this value implements `FnOnce`, which causes it to be moved when called
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9
|
||||
|
|
||||
LL | var();
|
||||
| ^^^
|
||||
|
||||
error[E0618]: expected function, found `impl Sized`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9
|
||||
|
|
||||
LL | fn opaque_type_no_impl_fn() -> impl Sized {
|
||||
| ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `impl Sized`
|
||||
LL | if false {
|
||||
LL | opaque_type_no_impl_fn()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error[E0618]: expected function, found `impl Sized`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9
|
||||
|
|
||||
LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized {
|
||||
| --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `impl Sized`
|
||||
LL | if false {
|
||||
LL | opaque_type_no_impl_fn_incorrect()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error[E0618]: expected function, found `impl Deref<Target = impl Sized>`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9
|
||||
|
|
||||
LL | fn opaque_type_deref_no_impl_fn() -> impl Deref<Target = impl Sized> {
|
||||
| -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `impl Deref<Target = impl Sized>`
|
||||
LL | if false {
|
||||
LL | opaque_type_deref_no_impl_fn()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0382, E0618.
|
||||
For more information about an error, try `rustc --explain E0382`.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
error[E0382]: use of moved value: `var`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9
|
||||
|
|
||||
LL | let mut var = item_bound_is_too_weak();
|
||||
| ------- move occurs because `var` has type `{closure@$DIR/call-expr-incorrect-choice-diagnostics.rs:19:5: 19:12}`, which does not implement the `Copy` trait
|
||||
LL | var();
|
||||
| ----- `var` moved due to this call
|
||||
LL | var();
|
||||
| ^^^ value used here after move
|
||||
|
|
||||
note: this value implements `FnOnce`, which causes it to be moved when called
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9
|
||||
|
|
||||
LL | var();
|
||||
| ^^^
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | var.clone()();
|
||||
| ++++++++
|
||||
|
||||
error[E0618]: expected function, found `_`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9
|
||||
|
|
||||
LL | fn opaque_type_no_impl_fn() -> impl Sized {
|
||||
| ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `_`
|
||||
LL | if false {
|
||||
LL | opaque_type_no_impl_fn()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error[E0618]: expected function, found `_`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9
|
||||
|
|
||||
LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized {
|
||||
| --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `_`
|
||||
LL | if false {
|
||||
LL | opaque_type_no_impl_fn_incorrect()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error[E0618]: expected function, found `_`
|
||||
--> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9
|
||||
|
|
||||
LL | fn opaque_type_deref_no_impl_fn() -> impl Deref<Target = impl Sized> {
|
||||
| -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `_`
|
||||
LL | if false {
|
||||
LL | opaque_type_deref_no_impl_fn()();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0382, E0618.
|
||||
For more information about an error, try `rustc --explain E0382`.
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
|
||||
// Testing the errors in case we've made a wrong choice when
|
||||
// calling an opaque.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
fn item_bound_is_too_weak() -> impl FnOnce() {
|
||||
if false {
|
||||
let mut var = item_bound_is_too_weak();
|
||||
var();
|
||||
var();
|
||||
//~^ ERROR use of moved value: `var`
|
||||
}
|
||||
|
||||
let mut state = String::new();
|
||||
move || state.push('a')
|
||||
}
|
||||
|
||||
fn opaque_type_no_impl_fn() -> impl Sized {
|
||||
if false {
|
||||
opaque_type_no_impl_fn()();
|
||||
//[current]~^ ERROR expected function, found `impl Sized`
|
||||
//[next]~^^ ERROR expected function, found `_`
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
fn opaque_type_no_impl_fn_incorrect() -> impl Sized {
|
||||
if false {
|
||||
opaque_type_no_impl_fn_incorrect()();
|
||||
//[current]~^ ERROR expected function, found `impl Sized`
|
||||
//[next]~^^ ERROR expected function, found `_`
|
||||
}
|
||||
|
||||
|| ()
|
||||
}
|
||||
|
||||
fn opaque_type_deref_no_impl_fn() -> impl Deref<Target = impl Sized> {
|
||||
if false {
|
||||
opaque_type_deref_no_impl_fn()();
|
||||
//[current]~^ ERROR expected function, found `impl Deref<Target = impl Sized>`
|
||||
//[next]~^^ ERROR expected function, found `_`
|
||||
}
|
||||
|
||||
&1
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
error[E0599]: no method named `len` found for struct `Wrapper<T>` in the current scope
|
||||
--> $DIR/deref-constrains-self-ty.rs:22:32
|
||||
|
|
||||
LL | struct Wrapper<T>(T);
|
||||
| ----------------- method `len` not found for this struct
|
||||
...
|
||||
LL | let _ = Wrapper(foo()).len();
|
||||
| ^^^ method not found in `Wrapper<impl Sized>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `len`, perhaps you need to implement it:
|
||||
candidate #1: `ExactSizeIterator`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] check-pass
|
||||
|
||||
// A test which shows that autoderef can constrain opaque types even
|
||||
// though it's supposed to treat not-yet-defined opaque types as
|
||||
// mostly rigid. I don't think this should necessarily compile :shrug:
|
||||
use std::ops::Deref;
|
||||
|
||||
struct Wrapper<T>(T);
|
||||
|
||||
impl<T> Deref for Wrapper<Vec<T>> {
|
||||
type Target = Vec<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn foo() -> impl Sized {
|
||||
if false {
|
||||
let _ = Wrapper(foo()).len();
|
||||
//[current]~^ ERROR no method named `len` found for struct `Wrapper<T>` in the current scope
|
||||
}
|
||||
|
||||
std::iter::once(1).collect()
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
error[E0792]: expected generic type parameter, found `impl Foo`
|
||||
--> $DIR/double-wrap-with-defining-use.rs:12:26
|
||||
--> $DIR/double-wrap-with-defining-use.rs:11:26
|
||||
|
|
||||
LL | fn a<T: Foo>(x: T) -> impl Foo {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
|
|
@ -7,7 +7,7 @@ LL | if true { x } else { a(a(x)) }
|
|||
| ^^^^^^^
|
||||
|
||||
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
|
||||
--> $DIR/double-wrap-with-defining-use.rs:12:26
|
||||
--> $DIR/double-wrap-with-defining-use.rs:11:26
|
||||
|
|
||||
LL | if true { x } else { a(a(x)) }
|
||||
| ^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Regression test for ICE from issue #140545
|
||||
// The error message is confusing and wrong, but that's a different problem (#139350)
|
||||
|
||||
//@ edition:2018
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#181. Make sure calling
|
||||
// opaque types works.
|
||||
|
||||
fn fn_trait() -> impl Fn() {
|
||||
if false {
|
||||
let f = fn_trait();
|
||||
f();
|
||||
}
|
||||
|
||||
|| ()
|
||||
}
|
||||
|
||||
fn fn_trait_ref() -> impl Fn() {
|
||||
if false {
|
||||
let f = &fn_trait();
|
||||
f();
|
||||
}
|
||||
|| ()
|
||||
}
|
||||
|
||||
fn fn_mut() -> impl FnMut() -> usize {
|
||||
if false {
|
||||
let mut f = fn_mut();
|
||||
f();
|
||||
}
|
||||
|
||||
let mut state = 0;
|
||||
move || {
|
||||
state += 1;
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_mut_ref() -> impl FnMut() -> usize {
|
||||
if false {
|
||||
let mut f = &mut fn_mut();
|
||||
f();
|
||||
}
|
||||
|
||||
let mut state = 0;
|
||||
move || {
|
||||
state += 1;
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn fn_once() -> impl FnOnce() {
|
||||
if false {
|
||||
let mut f = fn_once();
|
||||
f();
|
||||
}
|
||||
|
||||
let string = String::new();
|
||||
move || drop(string)
|
||||
}
|
||||
|
||||
fn fn_once_ref() -> impl FnOnce() {
|
||||
if false {
|
||||
let mut f = Box::new(fn_once_ref());
|
||||
f();
|
||||
}
|
||||
|
||||
let string = String::new();
|
||||
move || drop(string)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -5,7 +5,7 @@ LL | fn create_complex_future() -> impl Future<Output = impl ReturnsSend> {
|
|||
| ^^^^^^^^^^^^^^^^ the trait `ReturnsSend` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/ice-issue-146191.rs:14:1
|
||||
--> $DIR/ice-issue-146191.rs:13:1
|
||||
|
|
||||
LL | trait ReturnsSend {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -4,14 +4,6 @@ error[E0282]: type annotations needed
|
|||
LL | async { create_complex_future().await }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/ice-issue-146191.rs:8:5
|
||||
|
|
||||
LL | async { create_complex_future().await }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0282`.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ fn create_complex_future() -> impl Future<Output = impl ReturnsSend> {
|
|||
async { create_complex_future().await }
|
||||
//[current]~^ ERROR recursion in an async block requires
|
||||
//[next]~^^ ERROR type annotations needed
|
||||
//[next]~| ERROR type annotations needed
|
||||
}
|
||||
|
||||
trait ReturnsSend {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#181. We want to
|
||||
// be able to step through `impl Deref` in its defining scope.
|
||||
use std::ops::{Deref, DerefMut};
|
||||
fn impl_deref_fn() -> impl Deref<Target = fn(fn(&str) -> usize)> {
|
||||
if false {
|
||||
let func = impl_deref_fn();
|
||||
func(|s| s.len());
|
||||
}
|
||||
|
||||
&((|_| ()) as fn(_))
|
||||
}
|
||||
|
||||
fn impl_deref_impl_fn() -> impl Deref<Target = impl Fn()> {
|
||||
if false {
|
||||
let func = impl_deref_impl_fn();
|
||||
func();
|
||||
}
|
||||
|
||||
&|| ()
|
||||
}
|
||||
|
||||
fn impl_deref_impl_deref_impl_fn() -> impl Deref<Target = impl Deref<Target = impl Fn()>> {
|
||||
if false {
|
||||
let func = impl_deref_impl_deref_impl_fn();
|
||||
func();
|
||||
}
|
||||
|
||||
&&|| ()
|
||||
}
|
||||
|
||||
|
||||
fn impl_deref_mut_impl_fn() -> impl DerefMut<Target = impl Fn()> {
|
||||
if false {
|
||||
let func = impl_deref_impl_fn();
|
||||
func();
|
||||
}
|
||||
|
||||
Box::new(|| ())
|
||||
}
|
||||
|
||||
|
||||
fn impl_deref_mut_impl_fn_mut() -> impl DerefMut<Target = impl FnMut()> {
|
||||
if false {
|
||||
let mut func = impl_deref_mut_impl_fn_mut();
|
||||
func();
|
||||
}
|
||||
|
||||
let mut state = 0;
|
||||
Box::new(move || state += 1)
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#181.
|
||||
|
||||
struct ShExCompactPrinter;
|
||||
|
||||
struct TripleExpr;
|
||||
|
||||
impl ShExCompactPrinter {
|
||||
fn pp_triple_expr(&self) -> impl Fn(&TripleExpr, &ShExCompactPrinter) + '_ {
|
||||
move |te, printer| {
|
||||
printer.pp_triple_expr()(te, printer);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -4,12 +4,6 @@ error[E0282]: type annotations needed
|
|||
LL | fn muh(x: A) -> B {
|
||||
| ^ cannot infer type
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/two_tait_defining_each_other2.rs:14:5
|
||||
|
|
||||
LL | x // B's hidden type is A (opaquely)
|
||||
| ^ cannot infer type
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0282`.
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ trait Foo {}
|
|||
fn muh(x: A) -> B {
|
||||
//[next]~^ ERROR: type annotations needed
|
||||
x // B's hidden type is A (opaquely)
|
||||
//[next]~^ ERROR: type annotations needed
|
||||
//[current]~^^ ERROR opaque type's hidden type cannot be another opaque type
|
||||
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
error[E0282]: type annotations needed
|
||||
--> $DIR/return-block-type-inference-15965.rs:5:9
|
||||
|
|
||||
LL | / { return () }
|
||||
LL | |
|
||||
LL | | ()
|
||||
| |______^ cannot infer type
|
||||
LL | { return () }
|
||||
| ^^^^^^^^^^^^^ cannot infer type
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ LL | let mut closure0 = None;
|
|||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | return c();
|
||||
| --- type must be known at this point
|
||||
| - type must be known at this point
|
||||
|
|
||||
help: consider giving `closure0` an explicit type, where the placeholders `_` are specified
|
||||
|
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue