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:
bors 2025-09-18 13:49:53 +00:00
commit 4cd91ef822
41 changed files with 965 additions and 162 deletions

View file

@ -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;
}

View file

@ -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)),

View file

@ -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(

View file

@ -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;

View file

@ -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);

View file

@ -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,
};

View file

@ -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`

View file

@ -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,

View file

@ -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());

View file

@ -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)
}));
}
}

View file

@ -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 },
)
}

View file

@ -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![]
}
}
}

View file

@ -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,
}

View file

@ -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 { .. })
})
}

View file

@ -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),
}
}

View file

@ -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(()),
}

View file

@ -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) => {

View file

@ -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| {

View file

@ -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`

View file

@ -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.

View file

@ -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,

View file

@ -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,
}
}
}

View file

@ -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`.

View 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() {}

View file

@ -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`.

View file

@ -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`.

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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)) }
| ^^^^^^^

View file

@ -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)

View file

@ -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() {}

View file

@ -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 {}
| ^^^^^^^^^^^^^^^^^

View file

@ -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`.

View file

@ -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 {}

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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`.

View file

@ -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;

View file

@ -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

View file

@ -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
|