Skip to content

Commit d327d65

Browse files
committed
Auto merge of #145711 - lcnr:non-defining-uses-hir-typeck, r=BoxyUwU
Support non-defining uses in HIR typeck This changes the impl of `NormalizesTo` for opaque types to be structural during HIR typeck. The previous impl equated region variables of the opaque type key with existing entries which can result in spurious leak check errors and also results in mismatches with MIR borrowck, theoretically causing ICE. The approach is very similar to #145244 in MIR typeck: - we collect all uses of opaque types during HIR typeck - before writeback, we search for *defining uses* - the opaque type key has fully universal generic args modulo regions - the hidden type has no infer vars - we use these defining uses to compute the concrete type for the opaque and map it to the definition site - we use this concrete type to check the type of all uses of opaques during HIR typeck. This also constrains infer vars in non-defining uses Fixes rust-lang/trait-system-refactor-initiative#135, fixes rust-lang/trait-system-refactor-initiative#49. r? `@BoxyUwU`
2 parents 54c5812 + d6a18e1 commit d327d65

35 files changed

+584
-397
lines changed

compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_middle::ty::{
1717
use rustc_mir_dataflow::points::DenseLocationMap;
1818
use rustc_span::Span;
1919
use rustc_trait_selection::opaque_types::{
20-
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
20+
NonDefiningUseReason, opaque_type_has_defining_use_args,
2121
};
2222
use rustc_trait_selection::solve::NoSolution;
2323
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
@@ -42,7 +42,7 @@ use region_ctxt::RegionCtxt;
4242
/// if there are no `RegionErrors`. If there are region errors, it's likely
4343
/// that errors here are caused by them and don't need to be handled separately.
4444
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
45-
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
45+
InvalidOpaqueTypeArgs(NonDefiningUseReason<'tcx>),
4646
LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
4747
UnexpectedHiddenRegion {
4848
/// The opaque type.
@@ -238,7 +238,7 @@ fn collect_defining_uses<'tcx>(
238238
let non_nll_opaque_type_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |r| {
239239
nll_var_to_universal_region(&rcx, r.as_var()).unwrap_or(r)
240240
});
241-
if let Err(err) = check_opaque_type_parameter_valid(
241+
if let Err(err) = opaque_type_has_defining_use_args(
242242
infcx,
243243
non_nll_opaque_type_key,
244244
hidden_type.span,
@@ -248,11 +248,10 @@ fn collect_defining_uses<'tcx>(
248248
// with `TypingMode::Borrowck`.
249249
if infcx.tcx.use_typing_mode_borrowck() {
250250
match err {
251-
InvalidOpaqueTypeArgs::AlreadyReported(guar) => root_cx
252-
.add_concrete_opaque_type(
253-
opaque_type_key.def_id,
254-
OpaqueHiddenType::new_error(infcx.tcx, guar),
255-
),
251+
NonDefiningUseReason::Tainted(guar) => root_cx.add_concrete_opaque_type(
252+
opaque_type_key.def_id,
253+
OpaqueHiddenType::new_error(infcx.tcx, guar),
254+
),
256255
_ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"),
257256
}
258257
} else {
@@ -676,8 +675,8 @@ impl<'tcx> InferCtxt<'tcx> {
676675
&self,
677676
opaque_type_key: OpaqueTypeKey<'tcx>,
678677
instantiated_ty: OpaqueHiddenType<'tcx>,
679-
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
680-
check_opaque_type_parameter_valid(
678+
) -> Result<Ty<'tcx>, NonDefiningUseReason<'tcx>> {
679+
opaque_type_has_defining_use_args(
681680
self,
682681
opaque_type_key,
683682
instantiated_ty.span,

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,10 +509,6 @@ hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by
509509
510510
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
511511
512-
hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`
513-
.note = consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]`
514-
.opaque = this opaque type is supposed to be constrained
515-
516512
hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`
517513
518514
hir_analysis_too_large_static = extern static is too large for the target architecture

compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravi
44
use rustc_middle::bug;
55
use rustc_middle::hir::nested_filter;
66
use rustc_middle::ty::{self, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt};
7+
use rustc_trait_selection::opaque_types::report_item_does_not_constrain_error;
78
use tracing::{debug, instrument, trace};
89

9-
use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
10+
use crate::errors::UnconstrainedOpaqueType;
1011

1112
/// Checks "defining uses" of opaque `impl Trait` in associated types.
1213
/// These can only be defined by associated items of the same trait.
@@ -127,14 +128,11 @@ impl<'tcx> TaitConstraintLocator<'tcx> {
127128
}
128129

129130
fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
130-
let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
131-
span: self
132-
.tcx
133-
.def_ident_span(item_def_id)
134-
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
135-
opaque_type_span: self.tcx.def_span(self.def_id),
136-
opaque_type: self.tcx.def_path_str(self.def_id),
137-
});
131+
// We make sure that all opaque types get defined while
132+
// type checking the defining scope, so this error is unreachable
133+
// with the new solver.
134+
assert!(!self.tcx.next_trait_solver_globally());
135+
let guar = report_item_does_not_constrain_error(self.tcx, item_def_id, self.def_id, None);
138136
self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar));
139137
}
140138

@@ -252,9 +250,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
252250
} else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
253251
hidden_ty.ty
254252
} else {
255-
// FIXME(-Znext-solver): This should not be necessary and we should
256-
// instead rely on inference variable fallback inside of typeck itself.
257-
253+
assert!(!tcx.next_trait_solver_globally());
258254
// We failed to resolve the opaque type or it
259255
// resolves to itself. We interpret this as the
260256
// no values of the hidden type ever being constructed,
@@ -273,6 +269,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
273269
if let Err(guar) = hir_ty.error_reported() {
274270
Ty::new_error(tcx, guar)
275271
} else {
272+
assert!(!tcx.next_trait_solver_globally());
276273
hir_ty
277274
}
278275
}

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -410,17 +410,6 @@ pub(crate) struct UnconstrainedOpaqueType {
410410
pub what: &'static str,
411411
}
412412

413-
#[derive(Diagnostic)]
414-
#[diag(hir_analysis_tait_forward_compat2)]
415-
#[note]
416-
pub(crate) struct TaitForwardCompat2 {
417-
#[primary_span]
418-
pub span: Span,
419-
#[note(hir_analysis_opaque)]
420-
pub opaque_type_span: Span,
421-
pub opaque_type: String,
422-
}
423-
424413
pub(crate) struct MissingTypeParams {
425414
pub span: Span,
426415
pub def_span: Span,

compiler/rustc_hir_analysis/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ mod coherence;
8282
mod collect;
8383
mod constrained_generic_params;
8484
mod delegation;
85-
mod errors;
85+
pub mod errors;
8686
pub mod hir_ty_lowering;
8787
pub mod hir_wf_check;
8888
mod impl_wf_check;

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,13 @@ fn typeck_with_inspect<'tcx>(
247247

248248
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
249249

250+
// We need to handle opaque types before emitting ambiguity errors as applying
251+
// defining uses may guide type inference.
252+
if fcx.next_trait_solver() {
253+
fcx.handle_opaque_type_uses_next();
254+
}
255+
256+
fcx.select_obligations_where_possible(|_| {});
250257
if let None = fcx.infcx.tainted_by_errors() {
251258
fcx.report_ambiguity_errors();
252259
}

compiler/rustc_hir_typeck/src/opaque_types.rs

Lines changed: 214 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,218 @@
1-
use super::FnCtxt;
1+
use rustc_hir::def::DefKind;
2+
use rustc_infer::traits::ObligationCause;
3+
use rustc_middle::ty::{
4+
self, DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, TypeVisitableExt,
5+
TypingMode,
6+
};
7+
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
8+
use rustc_trait_selection::opaque_types::{
9+
NonDefiningUseReason, opaque_type_has_defining_use_args, report_item_does_not_constrain_error,
10+
};
11+
use rustc_trait_selection::solve;
12+
use tracing::{debug, instrument};
13+
14+
use crate::FnCtxt;
15+
216
impl<'tcx> FnCtxt<'_, 'tcx> {
17+
/// This takes all the opaque type uses during HIR typeck. It first computes
18+
/// the concrete hidden type by iterating over all defining uses.
19+
///
20+
/// A use during HIR typeck is defining if all non-lifetime arguments are
21+
/// unique generic parameters and the hidden type does not reference any
22+
/// inference variables.
23+
///
24+
/// It then uses these defining uses to guide inference for all other uses.
25+
#[instrument(level = "debug", skip(self))]
26+
pub(super) fn handle_opaque_type_uses_next(&mut self) {
27+
// We clone the opaques instead of stealing them here as they are still used for
28+
// normalization in the next generation trait solver.
29+
let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types();
30+
let num_entries = self.inner.borrow_mut().opaque_types().num_entries();
31+
let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries));
32+
debug_assert_eq!(prev, None);
33+
for entry in &mut opaque_types {
34+
*entry = self.resolve_vars_if_possible(*entry);
35+
}
36+
debug!(?opaque_types);
37+
38+
self.compute_concrete_opaque_types(&opaque_types);
39+
self.apply_computed_concrete_opaque_types(&opaque_types);
40+
}
41+
}
42+
43+
enum UsageKind<'tcx> {
44+
None,
45+
NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>),
46+
UnconstrainedHiddenType(OpaqueHiddenType<'tcx>),
47+
HasDefiningUse,
48+
}
49+
50+
impl<'tcx> UsageKind<'tcx> {
51+
fn merge(&mut self, other: UsageKind<'tcx>) {
52+
match (&*self, &other) {
53+
(UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(),
54+
(UsageKind::None, _) => *self = other,
55+
// When mergining non-defining uses, prefer earlier ones. This means
56+
// the error happens as early as possible.
57+
(
58+
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
59+
UsageKind::NonDefiningUse(..),
60+
) => {}
61+
// When merging unconstrained hidden types, we prefer later ones. This is
62+
// used as in most cases, the defining use is the final return statement
63+
// of our function, and other uses with defining arguments are likely not
64+
// intended to be defining.
65+
(
66+
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
67+
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse,
68+
) => *self = other,
69+
}
70+
}
71+
}
72+
73+
impl<'tcx> FnCtxt<'_, 'tcx> {
74+
fn compute_concrete_opaque_types(
75+
&mut self,
76+
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
77+
) {
78+
let tcx = self.tcx;
79+
let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
80+
else {
81+
unreachable!();
82+
};
83+
84+
for def_id in defining_opaque_types_and_generators {
85+
match tcx.def_kind(def_id) {
86+
DefKind::OpaqueTy => {}
87+
DefKind::Closure => continue,
88+
_ => unreachable!("not opaque or generator: {def_id:?}"),
89+
}
90+
91+
let mut usage_kind = UsageKind::None;
92+
for &(opaque_type_key, hidden_type) in opaque_types {
93+
if opaque_type_key.def_id != def_id {
94+
continue;
95+
}
96+
97+
usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
98+
if let UsageKind::HasDefiningUse = usage_kind {
99+
break;
100+
}
101+
}
102+
103+
let guar = match usage_kind {
104+
UsageKind::None => {
105+
if let Some(guar) = self.tainted_by_errors() {
106+
guar
107+
} else {
108+
report_item_does_not_constrain_error(self.tcx, self.body_id, def_id, None)
109+
}
110+
}
111+
UsageKind::NonDefiningUse(opaque_type_key, hidden_type) => {
112+
report_item_does_not_constrain_error(
113+
self.tcx,
114+
self.body_id,
115+
def_id,
116+
Some((opaque_type_key, hidden_type.span)),
117+
)
118+
}
119+
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()
135+
}
136+
UsageKind::HasDefiningUse => continue,
137+
};
138+
139+
self.typeck_results
140+
.borrow_mut()
141+
.concrete_opaque_types
142+
.insert(def_id, OpaqueHiddenType::new_error(tcx, guar));
143+
self.set_tainted_by_errors(guar);
144+
}
145+
}
146+
147+
fn consider_opaque_type_use(
148+
&mut self,
149+
opaque_type_key: OpaqueTypeKey<'tcx>,
150+
hidden_type: OpaqueHiddenType<'tcx>,
151+
) -> UsageKind<'tcx> {
152+
if let Err(err) = opaque_type_has_defining_use_args(
153+
&self,
154+
opaque_type_key,
155+
hidden_type.span,
156+
DefiningScopeKind::HirTypeck,
157+
) {
158+
match err {
159+
NonDefiningUseReason::Tainted(guar) => {
160+
self.typeck_results.borrow_mut().concrete_opaque_types.insert(
161+
opaque_type_key.def_id,
162+
OpaqueHiddenType::new_error(self.tcx, guar),
163+
);
164+
return UsageKind::HasDefiningUse;
165+
}
166+
_ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type),
167+
};
168+
}
169+
170+
// We ignore uses of the opaque if they have any inference variables
171+
// as this can frequently happen with recursive calls.
172+
//
173+
// See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
174+
if hidden_type.ty.has_non_region_infer() {
175+
return UsageKind::UnconstrainedHiddenType(hidden_type);
176+
}
177+
178+
let cause = ObligationCause::misc(hidden_type.span, self.body_id);
179+
let at = self.at(&cause, self.param_env);
180+
let hidden_type = match solve::deeply_normalize(at, hidden_type) {
181+
Ok(hidden_type) => hidden_type,
182+
Err(errors) => {
183+
let guar = self.err_ctxt().report_fulfillment_errors(errors);
184+
OpaqueHiddenType::new_error(self.tcx, guar)
185+
}
186+
};
187+
let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
188+
opaque_type_key,
189+
self.tcx,
190+
DefiningScopeKind::HirTypeck,
191+
);
192+
193+
let prev = self
194+
.typeck_results
195+
.borrow_mut()
196+
.concrete_opaque_types
197+
.insert(opaque_type_key.def_id, hidden_type);
198+
assert!(prev.is_none());
199+
UsageKind::HasDefiningUse
200+
}
201+
202+
fn apply_computed_concrete_opaque_types(
203+
&mut self,
204+
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
205+
) {
206+
let tcx = self.tcx;
207+
for &(key, hidden_type) in opaque_types {
208+
let expected =
209+
*self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id).unwrap();
210+
211+
let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
212+
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
213+
}
214+
}
215+
3216
/// We may in theory add further uses of an opaque after cloning the opaque
4217
/// types storage during writeback when computing the defining uses.
5218
///

0 commit comments

Comments
 (0)