Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar here, I realise we previously only checked is_ty_var, but how does the rest of this codepath handle int/float vars

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they don't implement Deref so they are the final self type and users of autoderef have to handle them if necessary

&& !self.infcx.has_opaques_with_sub_unified_hidden_type(vid)
{
return None;
}

Expand Down Expand Up @@ -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;
}
Expand Down
26 changes: 22 additions & 4 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}

ty::Infer(ty::TyVar(vid)) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels subtle to me that we are specifically checking for TyVar instead of just all ty::Infer? How does the rest of the code handle int/float vars

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can never impl Fn and Deref, so 🤷 we generally accept int/float infer vars, e.g. structurally_resolve_type also does so

// 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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this means we continue to error for everything that has not been sub-unified with an opaque type

self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty);
return None;
}
}

ty::Error(_) => {
return None;
}
Expand Down Expand Up @@ -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)),
Expand Down
37 changes: 19 additions & 18 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
29 changes: 28 additions & 1 deletion compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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);
Expand Down
34 changes: 19 additions & 15 deletions compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
28 changes: 25 additions & 3 deletions compiler/rustc_hir_typeck/src/place_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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(
Expand All @@ -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`
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
54 changes: 54 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading
Loading