Skip to content

Commit 4cd91ef

Browse files
committed
Auto merge of #145993 - lcnr:allow-calling-opaques, r=BoxyUwU
`-Znext-solver` allow `ExprKind::Call` for not-yet defined opaques Based on #146329. Revival of #140496. See the comment on `OpaqueTypesJank`. I've used the following document while working on this https://hackmd.io/Js61f8PRTcyaiyqS-fH9iQ. Fixes rust-lang/trait-system-refactor-initiative#181. It does introduce one subtle footgun we may want to handle before stabilization, opened rust-lang/trait-system-refactor-initiative#230 for that. Also cc rust-lang/trait-system-refactor-initiative#231 for deref and index operations r? `@BoxyUwU`
2 parents 32e3d9f + 9913c47 commit 4cd91ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+965
-162
lines changed

compiler/rustc_hir_analysis/src/autoderef.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
6868
return None;
6969
}
7070

71-
if self.state.cur_ty.is_ty_var() {
71+
// We want to support method and function calls for `impl Deref<Target = ..>`.
72+
//
73+
// To do so we don't eagerly bail if the current type is the hidden type of an
74+
// opaque type and instead return `None` in `fn overloaded_deref_ty` if the
75+
// opaque does not have a `Deref` item-bound.
76+
if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind()
77+
&& !self.infcx.has_opaques_with_sub_unified_hidden_type(vid)
78+
{
7279
return None;
7380
}
7481

@@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
160167
self.param_env,
161168
ty::Binder::dummy(trait_ref),
162169
);
163-
if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) {
170+
// We detect whether the self type implements `Deref` before trying to
171+
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
172+
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
173+
// but fail for `impl OtherTrait`.
174+
if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) {
164175
debug!("overloaded_deref_ty: cannot match obligation");
165176
return None;
166177
}

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use tracing::{debug, instrument};
2525
use super::method::MethodCallee;
2626
use super::method::probe::ProbeScope;
2727
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
28+
use crate::method::TreatNotYetDefinedOpaques;
2829
use crate::{errors, fluent_generated};
2930

3031
/// Checks that it is legal to call methods of the trait corresponding
@@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7879
_ => self.check_expr(callee_expr),
7980
};
8081

81-
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
82+
let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty);
8283

8384
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
8485
let mut result = None;
@@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
200201
arg_exprs: &'tcx [hir::Expr<'tcx>],
201202
autoderef: &Autoderef<'a, 'tcx>,
202203
) -> Option<CallStep<'tcx>> {
203-
let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty());
204+
let adjusted_ty =
205+
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty());
204206

205207
// If the callee is a function pointer or a closure, then we're all set.
206208
match *adjusted_ty.kind() {
@@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
297299
return None;
298300
}
299301

302+
ty::Infer(ty::TyVar(vid)) => {
303+
// If we end up with an inference variable which is not the hidden type of
304+
// an opaque, emit an error.
305+
if !self.has_opaques_with_sub_unified_hidden_type(vid) {
306+
self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty);
307+
return None;
308+
}
309+
}
310+
300311
ty::Error(_) => {
301312
return None;
302313
}
@@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
367378
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
368379
});
369380

381+
// We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty`
382+
// is `Box<impl FnOnce()>` we choose `FnOnce` instead of `Fn`.
383+
//
384+
// We try all the different call traits in order and choose the first
385+
// one which may apply. So if we treat opaques as inference variables
386+
// `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen.
370387
if let Some(ok) = self.lookup_method_for_operator(
371388
self.misc(call_expr.span),
372389
method_name,
373390
trait_def_id,
374391
adjusted_ty,
375392
opt_input_type,
393+
TreatNotYetDefinedOpaques::AsRigid,
376394
) {
377395
let method = self.register_infer_ok_obligations(ok);
378396
let mut autoref = None;
379397
if borrow {
380398
// Check for &self vs &mut self in the method signature. Since this is either
381399
// the Fn or FnMut trait, it should be one of those.
382-
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
400+
let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else {
383401
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
384402
};
385403

386404
// For initial two-phase borrow
387405
// deployment, conservatively omit
388406
// overloaded function call ops.
389-
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);
407+
let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No);
390408

391409
autoref = Some(Adjustment {
392410
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14691469
pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
14701470
let ty = self.try_structurally_resolve_type(sp, ty);
14711471

1472-
if !ty.is_ty_var() {
1473-
ty
1474-
} else {
1475-
let e = self.tainted_by_errors().unwrap_or_else(|| {
1476-
self.err_ctxt()
1477-
.emit_inference_failure_err(
1478-
self.body_id,
1479-
sp,
1480-
ty.into(),
1481-
TypeAnnotationNeeded::E0282,
1482-
true,
1483-
)
1484-
.emit()
1485-
});
1486-
let err = Ty::new_error(self.tcx, e);
1487-
self.demand_suptype(sp, err, ty);
1488-
err
1489-
}
1472+
if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) }
1473+
}
1474+
1475+
#[cold]
1476+
pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
1477+
let guar = self.tainted_by_errors().unwrap_or_else(|| {
1478+
self.err_ctxt()
1479+
.emit_inference_failure_err(
1480+
self.body_id,
1481+
sp,
1482+
ty.into(),
1483+
TypeAnnotationNeeded::E0282,
1484+
true,
1485+
)
1486+
.emit()
1487+
});
1488+
let err = Ty::new_error(self.tcx, guar);
1489+
self.demand_suptype(sp, err, ty);
1490+
err
14901491
}
14911492

14921493
pub(crate) fn structurally_resolve_const(

compiler/rustc_hir_typeck/src/method/mod.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
317317
)?;
318318
Ok(pick)
319319
}
320+
}
321+
322+
/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`.
323+
///
324+
/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while
325+
/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary
326+
/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque
327+
/// types as rigid to support `impl Deref<Target = impl FnOnce()>` and
328+
/// `Box<impl FnOnce()>`.
329+
///
330+
/// We only want to treat opaque types as rigid if we need to eagerly choose
331+
/// between multiple candidates. We otherwise treat them as ordinary inference
332+
/// variable to avoid rejecting otherwise correct code.
333+
#[derive(Debug)]
334+
pub(super) enum TreatNotYetDefinedOpaques {
335+
AsInfer,
336+
AsRigid,
337+
}
320338

339+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
321340
/// `lookup_method_in_trait` is used for overloaded operators.
322341
/// It does a very narrow slice of what the normal probe/confirm path does.
323342
/// In particular, it doesn't really do any probing: it simply constructs
@@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
331350
trait_def_id: DefId,
332351
self_ty: Ty<'tcx>,
333352
opt_rhs_ty: Option<Ty<'tcx>>,
353+
treat_opaques: TreatNotYetDefinedOpaques,
334354
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
335355
// Construct a trait-reference `self_ty : Trait<input_tys>`
336356
let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
@@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
360380
);
361381

362382
// Now we want to know if this can be matched
363-
if !self.predicate_may_hold(&obligation) {
383+
let matches_trait = match treat_opaques {
384+
TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation),
385+
TreatNotYetDefinedOpaques::AsRigid => {
386+
self.predicate_may_hold_opaque_types_jank(&obligation)
387+
}
388+
};
389+
390+
if !matches_trait {
364391
debug!("--> Cannot match obligation");
365392
// Cannot be matched, no such method resolution is possible.
366393
return None;

compiler/rustc_hir_typeck/src/op.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir};
2121
use super::FnCtxt;
2222
use super::method::MethodCallee;
2323
use crate::Expectation;
24+
use crate::method::TreatNotYetDefinedOpaques;
2425

2526
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2627
/// Checks a `a <op>= b`
@@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
974975
},
975976
);
976977

977-
let method =
978-
self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
978+
// We don't consider any other candidates if this lookup fails
979+
// so we can freely treat opaque types as inference variables here
980+
// to allow more code to compile.
981+
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
982+
let method = self.lookup_method_for_operator(
983+
cause.clone(),
984+
opname,
985+
trait_did,
986+
lhs_ty,
987+
opt_rhs_ty,
988+
treat_opaques,
989+
);
979990
match method {
980991
Some(ok) => {
981992
let method = self.register_infer_ok_obligations(ok);

compiler/rustc_hir_typeck/src/opaque_types.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
117117
)
118118
}
119119
UsageKind::UnconstrainedHiddenType(hidden_type) => {
120-
let infer_var = hidden_type
121-
.ty
122-
.walk()
123-
.filter_map(ty::GenericArg::as_term)
124-
.find(|term| term.is_infer())
125-
.unwrap_or_else(|| hidden_type.ty.into());
126-
self.err_ctxt()
127-
.emit_inference_failure_err(
128-
self.body_id,
129-
hidden_type.span,
130-
infer_var,
131-
TypeAnnotationNeeded::E0282,
132-
false,
133-
)
134-
.emit()
120+
if let Some(guar) = self.tainted_by_errors() {
121+
guar
122+
} else {
123+
let infer_var = hidden_type
124+
.ty
125+
.walk()
126+
.filter_map(ty::GenericArg::as_term)
127+
.find(|term| term.is_infer())
128+
.unwrap_or_else(|| hidden_type.ty.into());
129+
self.err_ctxt()
130+
.emit_inference_failure_err(
131+
self.body_id,
132+
hidden_type.span,
133+
infer_var,
134+
TypeAnnotationNeeded::E0282,
135+
false,
136+
)
137+
.emit()
138+
}
135139
}
136140
UsageKind::HasDefiningUse => continue,
137141
};

compiler/rustc_hir_typeck/src/place_op.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_span::{Span, sym};
1212
use tracing::debug;
1313
use {rustc_ast as ast, rustc_hir as hir};
1414

15-
use crate::method::MethodCallee;
15+
use crate::method::{MethodCallee, TreatNotYetDefinedOpaques};
1616
use crate::{FnCtxt, PlaceOp};
1717

1818
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
210210
return None;
211211
};
212212

213-
self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty)
213+
// FIXME(trait-system-refactor-initiative#231): we may want to treat
214+
// opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`.
215+
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
216+
self.lookup_method_for_operator(
217+
self.misc(span),
218+
imm_op,
219+
imm_tr,
220+
base_ty,
221+
opt_rhs_ty,
222+
treat_opaques,
223+
)
214224
}
215225

216226
fn try_mutable_overloaded_place_op(
@@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
230240
return None;
231241
};
232242

233-
self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty)
243+
// We have to replace the operator with the mutable variant for the
244+
// program to compile, so we don't really have a choice here and want
245+
// to just try using `DerefMut` even if its not in the item bounds
246+
// of the opaque.
247+
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
248+
self.lookup_method_for_operator(
249+
self.misc(span),
250+
mut_op,
251+
mut_tr,
252+
base_ty,
253+
opt_rhs_ty,
254+
treat_opaques,
255+
)
234256
}
235257

236258
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`

compiler/rustc_infer/src/infer/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
302302
.map(|(k, h)| (k, h.ty))
303303
.collect()
304304
}
305+
fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec<ty::AliasTy<'tcx>> {
306+
self.opaques_with_sub_unified_hidden_type(ty)
307+
}
305308

306309
fn register_hidden_type_in_storage(
307310
&self,

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> {
10041004
self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
10051005
}
10061006

1007+
pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool {
1008+
if !self.next_trait_solver() {
1009+
return false;
1010+
}
1011+
1012+
let ty_sub_vid = self.sub_unification_table_root_var(ty_vid);
1013+
let inner = &mut *self.inner.borrow_mut();
1014+
let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
1015+
inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| {
1016+
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
1017+
let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
1018+
if opaque_sub_vid == ty_sub_vid {
1019+
return true;
1020+
}
1021+
}
1022+
1023+
false
1024+
})
1025+
}
1026+
1027+
/// Searches for an opaque type key whose hidden type is related to `ty_vid`.
1028+
///
1029+
/// This only checks for a subtype relation, it does not require equality.
1030+
pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec<ty::AliasTy<'tcx>> {
1031+
// Avoid accidentally allowing more code to compile with the old solver.
1032+
if !self.next_trait_solver() {
1033+
return vec![];
1034+
}
1035+
1036+
let ty_sub_vid = self.sub_unification_table_root_var(ty_vid);
1037+
let inner = &mut *self.inner.borrow_mut();
1038+
// This is iffy, can't call `type_variables()` as we're already
1039+
// borrowing the `opaque_type_storage` here.
1040+
let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
1041+
inner
1042+
.opaque_type_storage
1043+
.iter_opaque_types()
1044+
.filter_map(|(key, hidden_ty)| {
1045+
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
1046+
let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
1047+
if opaque_sub_vid == ty_sub_vid {
1048+
return Some(ty::AliasTy::new_from_args(
1049+
self.tcx,
1050+
key.def_id.into(),
1051+
key.args,
1052+
));
1053+
}
1054+
}
1055+
1056+
None
1057+
})
1058+
.collect()
1059+
}
1060+
10071061
#[inline(always)]
10081062
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
10091063
debug_assert!(!self.next_trait_solver());

0 commit comments

Comments
 (0)