Skip to content

Commit 93ea836

Browse files
committed
support revealing uses in HIR typeck
1 parent e69b2c3 commit 93ea836

File tree

7 files changed

+183
-121
lines changed

7 files changed

+183
-121
lines changed

compiler/rustc_hir_typeck/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ fn typeck_with_inspect<'tcx>(
231231

232232
fcx.select_obligations_where_possible(|_| {});
233233

234+
if fcx.next_trait_solver() {
235+
fcx.handle_opaque_type_uses_next();
236+
}
237+
234238
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
235239

236240
// This must be the last thing before `report_ambiguity_errors`.

compiler/rustc_hir_typeck/src/opaque_types.rs

+120-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,123 @@
1-
use super::FnCtxt;
2-
impl<'tcx> FnCtxt<'_, 'tcx> {
1+
use rustc_middle::ty::{
2+
DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, Ty, TypeVisitableExt,
3+
};
4+
use rustc_trait_selection::opaque_types::{
5+
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
6+
};
7+
use tracing::{debug, instrument};
8+
9+
use crate::FnCtxt;
10+
11+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12+
#[instrument(level = "debug", skip(self))]
13+
pub(super) fn handle_opaque_type_uses_next(&mut self) {
14+
// We clone the opaques instead of stealing them here as they are still used for
15+
// normalization in the next generation trait solver.
16+
//
17+
// FIXME(-Znext-solver): Opaque types defined after this would simply get dropped
18+
// at the end of typeck. Ideally we can feed some query here to no longer define
19+
// new opaque uses but instead always reveal by using the definitions inferred here.
20+
let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types();
21+
let num_entries = self.inner.borrow_mut().opaque_types().num_entries();
22+
let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries));
23+
debug_assert_eq!(prev, None);
24+
for entry in &mut opaque_types {
25+
*entry = self.resolve_vars_if_possible(*entry);
26+
}
27+
debug!(?opaque_types);
28+
29+
self.collect_defining_uses(&opaque_types);
30+
self.apply_defining_uses(&opaque_types);
31+
}
32+
33+
fn collect_defining_uses(
34+
&mut self,
35+
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
36+
) {
37+
let tcx = self.tcx;
38+
let typeck_results = &mut *self.typeck_results.borrow_mut();
39+
for &(opaque_type_key, hidden_type) in opaque_types {
40+
match check_opaque_type_parameter_valid(
41+
&self,
42+
opaque_type_key,
43+
hidden_type.span,
44+
DefiningScopeKind::HirTypeck,
45+
) {
46+
Ok(()) => {}
47+
Err(InvalidOpaqueTypeArgs::AlreadyReported(guar)) => {
48+
typeck_results
49+
.concrete_opaque_types
50+
.insert(opaque_type_key.def_id, OpaqueHiddenType::new_error(tcx, guar));
51+
}
52+
// Not a defining use, ignore and treat as revealing use instead.
53+
Err(
54+
InvalidOpaqueTypeArgs::NotAParam { .. }
55+
| InvalidOpaqueTypeArgs::DuplicateParam { .. },
56+
) => continue,
57+
}
58+
59+
// We ignore uses of the opaque if they have any inference variables
60+
// as this can frequently happen with recursive calls.
61+
//
62+
// See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
63+
if hidden_type.ty.has_non_region_infer() {
64+
continue;
65+
}
66+
67+
let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
68+
opaque_type_key,
69+
tcx,
70+
DefiningScopeKind::HirTypeck,
71+
);
72+
73+
if let Some(prev) =
74+
typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type)
75+
{
76+
let entry =
77+
typeck_results.concrete_opaque_types.get_mut(&opaque_type_key.def_id).unwrap();
78+
if prev.ty != hidden_type.ty {
79+
if let Some(guar) = typeck_results.tainted_by_errors {
80+
entry.ty = Ty::new_error(tcx, guar);
81+
} else {
82+
let (Ok(guar) | Err(guar)) =
83+
prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit());
84+
entry.ty = Ty::new_error(tcx, guar);
85+
}
86+
}
87+
88+
// Pick a better span if there is one.
89+
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
90+
entry.span = prev.span.substitute_dummy(hidden_type.span);
91+
}
92+
}
93+
94+
// FIXME(-Znext-solver): Check that all opaques have been defined hre.
95+
}
96+
97+
fn apply_defining_uses(
98+
&mut self,
99+
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
100+
) {
101+
let tcx = self.tcx;
102+
for &(key, hidden_type) in opaque_types {
103+
let Some(&expected) =
104+
self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id)
105+
else {
106+
let guar =
107+
tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope");
108+
self.typeck_results
109+
.borrow_mut()
110+
.concrete_opaque_types
111+
.insert(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
112+
self.set_tainted_by_errors(guar);
113+
continue;
114+
};
115+
116+
let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
117+
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
118+
}
119+
}
120+
3121
/// We may in theory add further uses of an opaque after cloning the opaque
4122
/// types storage during writeback when computing the defining uses.
5123
///

compiler/rustc_hir_typeck/src/writeback.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -534,26 +534,34 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
534534
}
535535
}
536536

537+
fn visit_opaque_types_next(&mut self) {
538+
let fcx_typeck_results = self.fcx.typeck_results.borrow();
539+
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
540+
for (&def_id, &hidden_type) in &fcx_typeck_results.concrete_opaque_types {
541+
assert!(!hidden_type.has_infer());
542+
self.typeck_results.concrete_opaque_types.insert(def_id, hidden_type);
543+
}
544+
}
545+
537546
#[instrument(skip(self), level = "debug")]
538547
fn visit_opaque_types(&mut self) {
548+
if self.fcx.next_trait_solver() {
549+
return self.visit_opaque_types_next();
550+
}
551+
539552
let tcx = self.tcx();
540-
// We clone the opaques instead of stealing them here as they are still used for
541-
// normalization in the next generation trait solver.
542-
let opaque_types = self.fcx.infcx.clone_opaque_types();
553+
let opaque_types = self.fcx.infcx.take_opaque_types();
543554
let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries();
544555
let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries));
545556
debug_assert_eq!(prev, None);
546557
for (opaque_type_key, hidden_type) in opaque_types {
547558
let hidden_type = self.resolve(hidden_type, &hidden_type.span);
548559
let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span);
549-
550-
if !self.fcx.next_trait_solver() {
551-
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
552-
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
553-
&& alias_ty.args == opaque_type_key.args
554-
{
555-
continue;
556-
}
560+
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
561+
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
562+
&& alias_ty.args == opaque_type_key.args
563+
{
564+
continue;
557565
}
558566

559567
if let Err(err) = check_opaque_type_parameter_valid(

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

-20
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::ops::ControlFlow;
44
#[cfg(feature = "nightly")]
55
use rustc_macros::HashStable_NoContext;
66
use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack};
7-
use rustc_type_ir::fast_reject::DeepRejectCtxt;
87
use rustc_type_ir::inherent::*;
98
use rustc_type_ir::relate::Relate;
109
use rustc_type_ir::relate::solver_relating::RelateExt;
@@ -1067,25 +1066,6 @@ where
10671066
self.add_goals(GoalSource::AliasWellFormed, goals);
10681067
}
10691068

1070-
// Do something for each opaque/hidden pair defined with `def_id` in the
1071-
// current inference context.
1072-
pub(super) fn probe_existing_opaque_ty(
1073-
&mut self,
1074-
key: ty::OpaqueTypeKey<I>,
1075-
) -> Option<(ty::OpaqueTypeKey<I>, I::Ty)> {
1076-
let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter(
1077-
|(candidate_key, _)| {
1078-
candidate_key.def_id == key.def_id
1079-
&& DeepRejectCtxt::relate_rigid_rigid(self.cx())
1080-
.args_may_unify(candidate_key.args, key.args)
1081-
},
1082-
);
1083-
let first = matching.next();
1084-
let second = matching.next();
1085-
assert_eq!(second, None);
1086-
first
1087-
}
1088-
10891069
// Try to evaluate a const, or return `None` if the const is too generic.
10901070
// This doesn't mean the const isn't evaluatable, though, and should be treated
10911071
// as an ambiguity rather than no-solution.

compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs

+21-88
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Computes a normalizes-to (projection) goal for opaque types. This goal
22
//! behaves differently depending on the current `TypingMode`.
33
4-
use rustc_index::bit_set::GrowableBitSet;
54
use rustc_type_ir::inherent::*;
65
use rustc_type_ir::solve::GoalSource;
7-
use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
6+
use rustc_type_ir::{self as ty, GenericArgKind, Interner, TypingMode, fold_regions};
87

98
use crate::delegate::SolverDelegate;
10-
use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect};
9+
use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
1110

1211
impl<D, I> EvalCtxt<'_, D>
1312
where
@@ -49,54 +48,29 @@ where
4948
return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
5049
};
5150

52-
// FIXME: This may have issues when the args contain aliases...
53-
match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) {
54-
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
55-
return self.evaluate_added_goals_and_make_canonical_response(
56-
Certainty::AMBIGUOUS,
57-
);
58-
}
59-
Err(_) => {
60-
return Err(NoSolution);
61-
}
62-
Ok(()) => {}
63-
}
64-
// Prefer opaques registered already.
65-
let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
66-
// FIXME: This also unifies the previous hidden type with the expected.
67-
//
68-
// If that fails, we insert `expected` as a new hidden type instead of
69-
// eagerly emitting an error.
70-
let existing = self.probe_existing_opaque_ty(opaque_type_key);
71-
if let Some((candidate_key, candidate_ty)) = existing {
72-
return self
73-
.probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
74-
result: *result,
75-
})
76-
.enter(|ecx| {
77-
for (a, b) in std::iter::zip(
78-
candidate_key.args.iter(),
79-
opaque_type_key.args.iter(),
80-
) {
81-
ecx.eq(goal.param_env, a, b)?;
82-
}
83-
ecx.eq(goal.param_env, candidate_ty, expected)?;
84-
ecx.add_item_bounds_for_hidden_type(
85-
def_id.into(),
86-
candidate_key.args,
87-
goal.param_env,
88-
candidate_ty,
89-
);
90-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
91-
});
51+
// We structurally normalize the args so that we're able to detect defining uses
52+
// later on. It also reduces the amount of duplicate definitions in the
53+
// `opaque_type_storage`.
54+
let normalized_args =
55+
cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() {
56+
GenericArgKind::Lifetime(lt) => Ok(lt.into()),
57+
GenericArgKind::Type(ty) => {
58+
self.structurally_normalize_ty(goal.param_env, ty).map(Into::into)
59+
}
60+
GenericArgKind::Const(ct) => {
61+
self.structurally_normalize_const(goal.param_env, ct).map(Into::into)
62+
}
63+
}))?;
64+
65+
let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args };
66+
if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected)
67+
{
68+
self.eq(goal.param_env, expected, prev)?;
9269
}
9370

94-
// Otherwise, define a new opaque type
95-
let prev = self.register_hidden_type_in_storage(opaque_type_key, expected);
96-
assert_eq!(prev, None);
9771
self.add_item_bounds_for_hidden_type(
9872
def_id.into(),
99-
opaque_ty.args,
73+
normalized_args,
10074
goal.param_env,
10175
expected,
10276
);
@@ -168,44 +142,3 @@ where
168142
}
169143
}
170144
}
171-
172-
/// Checks whether each generic argument is simply a unique generic placeholder.
173-
///
174-
/// FIXME: Interner argument is needed to constrain the `I` parameter.
175-
fn uses_unique_placeholders_ignoring_regions<I: Interner>(
176-
_cx: I,
177-
args: I::GenericArgs,
178-
) -> Result<(), NotUniqueParam<I>> {
179-
let mut seen = GrowableBitSet::default();
180-
for arg in args.iter() {
181-
match arg.kind() {
182-
// Ignore regions, since we can't resolve those in a canonicalized
183-
// query in the trait solver.
184-
ty::GenericArgKind::Lifetime(_) => {}
185-
ty::GenericArgKind::Type(t) => match t.kind() {
186-
ty::Placeholder(p) => {
187-
if !seen.insert(p.var()) {
188-
return Err(NotUniqueParam::DuplicateParam(t.into()));
189-
}
190-
}
191-
_ => return Err(NotUniqueParam::NotParam(t.into())),
192-
},
193-
ty::GenericArgKind::Const(c) => match c.kind() {
194-
ty::ConstKind::Placeholder(p) => {
195-
if !seen.insert(p.var()) {
196-
return Err(NotUniqueParam::DuplicateParam(c.into()));
197-
}
198-
}
199-
_ => return Err(NotUniqueParam::NotParam(c.into())),
200-
},
201-
}
202-
}
203-
204-
Ok(())
205-
}
206-
207-
// FIXME: This should check for dupes and non-params first, then infer vars.
208-
enum NotUniqueParam<I: Interner> {
209-
DuplicateParam(I::GenericArg),
210-
NotParam(I::GenericArg),
211-
}

tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// of the async block. This caused borrowck of the recursive
44
// call to ICE.
55

6+
//@ revisions: current next
7+
//@ ignore-compare-mode-next-solver (explicit revisions)
8+
//@[next] compile-flags: -Znext-solver
69
//@ edition: 2021
710
//@ check-pass
811
async fn test() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
// The recursive call to `foo` results in the opaque type use `opaque<U, T> = ?unconstrained`.
7+
// This needs to be supported and treated as a revealing use.
8+
9+
fn foo<T, U>(b: bool) -> impl Sized {
10+
if b {
11+
foo::<U, T>(b);
12+
}
13+
1u16
14+
}
15+
16+
fn main() {}

0 commit comments

Comments
 (0)