Skip to content

Commit ed49386

Browse files
committed
Auto merge of rust-lang#136539 - matthewjasper:late-normalize-errors, r=compiler-errors
Emit dropck normalization errors in borrowck Borrowck generally assumes that any queries it runs for type checking will succeed, thinking that HIR typeck will have errored first if there was a problem. However as of rust-lang#98641, dropck isn't run on HIR, so there's no direct guarantee that it doesn't error. While a type being well-formed might be expected to ensure that its fields are well-formed, this is not the case for types containing a type projection: ```rust pub trait AuthUser { type Id; } pub trait AuthnBackend { type User: AuthUser; } pub struct AuthSession<Backend: AuthnBackend> { data: Option<<<Backend as AuthnBackend>::User as AuthUser>::Id>, } pub trait Authz: Sized { type AuthnBackend: AuthnBackend<User = Self>; } pub fn run_query<User: Authz>(auth: AuthSession<User::AuthnBackend>) {} // ^ No User: AuthUser bound is required or inferred. ``` While improvements to trait solving might fix this in the future, for now we go for a pragmatic solution of emitting an error from borrowck (by rerunning dropck outside of a query) and making drop elaboration check if an error has been emitted previously before panicking for a failed normalization. Closes rust-lang#103899 Closes rust-lang#135039 r? `@compiler-errors` (feel free to re-assign)
2 parents 5986ff0 + 49cf00c commit ed49386

20 files changed

+262
-109
lines changed

compiler/rustc_borrowck/src/type_check/liveness/trace.rs

+44-13
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ use rustc_index::bit_set::DenseBitSet;
33
use rustc_index::interval::IntervalSet;
44
use rustc_infer::infer::canonical::QueryRegionConstraints;
55
use rustc_infer::infer::outlives::for_liveness;
6-
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
6+
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location};
7+
use rustc_middle::span_bug;
78
use rustc_middle::traits::query::DropckOutlivesResult;
89
use rustc_middle::ty::relate::Relate;
910
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
1011
use rustc_mir_dataflow::ResultsCursor;
1112
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
1213
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
1314
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
14-
use rustc_span::DUMMY_SP;
15+
use rustc_span::{DUMMY_SP, Span};
16+
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
17+
use rustc_trait_selection::traits::ObligationCtxt;
18+
use rustc_trait_selection::traits::query::dropck_outlives;
1519
use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
1620
use tracing::debug;
1721

@@ -162,9 +166,10 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
162166
fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
163167
for local in boring_locals {
164168
let local_ty = self.cx.body.local_decls[local].ty;
169+
let local_span = self.cx.body.local_decls[local].source_info.span;
165170
let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
166171
let typeck = &self.cx.typeck;
167-
move || LivenessContext::compute_drop_data(typeck, local_ty)
172+
move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
168173
});
169174

170175
drop_data.dropck_result.report_overflows(
@@ -522,9 +527,10 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
522527
values::pretty_print_points(self.location_map, live_at.iter()),
523528
);
524529

530+
let local_span = self.body.local_decls()[dropped_local].source_info.span;
525531
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
526532
let typeck = &self.typeck;
527-
move || Self::compute_drop_data(typeck, dropped_ty)
533+
move || Self::compute_drop_data(typeck, dropped_ty, local_span)
528534
});
529535

530536
if let Some(data) = &drop_data.region_constraint_data {
@@ -589,19 +595,44 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
589595
}
590596
}
591597

592-
fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> {
593-
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
598+
fn compute_drop_data(
599+
typeck: &TypeChecker<'_, 'tcx>,
600+
dropped_ty: Ty<'tcx>,
601+
span: Span,
602+
) -> DropData<'tcx> {
603+
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty);
604+
605+
let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty });
594606

595-
match typeck
596-
.infcx
597-
.param_env
598-
.and(DropckOutlives { dropped_ty })
599-
.fully_perform(typeck.infcx, DUMMY_SP)
600-
{
607+
match op.fully_perform(typeck.infcx, DUMMY_SP) {
601608
Ok(TypeOpOutput { output, constraints, .. }) => {
602609
DropData { dropck_result: output, region_constraint_data: constraints }
603610
}
604-
Err(_) => DropData { dropck_result: Default::default(), region_constraint_data: None },
611+
Err(_) => {
612+
// We don't run dropck on HIR, and dropck looks inside fields of
613+
// types, so there's no guarantee that it succeeds. We also
614+
// can't rely on the the `ErrorGuaranteed` from `fully_perform` here
615+
// because it comes from delay_span_bug.
616+
let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx);
617+
let errors =
618+
match dropck_outlives::compute_dropck_outlives_with_errors(&ocx, op, span) {
619+
Ok(_) => ocx.select_all_or_error(),
620+
Err(e) => {
621+
if e.is_empty() {
622+
ocx.select_all_or_error()
623+
} else {
624+
e
625+
}
626+
}
627+
};
628+
629+
if !errors.is_empty() {
630+
typeck.infcx.err_ctxt().report_fulfillment_errors(errors);
631+
} else {
632+
span_bug!(span, "Rerunning drop data query produced no error.");
633+
}
634+
DropData { dropck_result: Default::default(), region_constraint_data: None }
635+
}
605636
}
606637
}
607638
}

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ rustc_queries! {
802802

803803
query adt_dtorck_constraint(
804804
key: DefId
805-
) -> Result<&'tcx DropckConstraint<'tcx>, NoSolution> {
805+
) -> &'tcx DropckConstraint<'tcx> {
806806
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
807807
}
808808

compiler/rustc_mir_transform/src/elaborate_drop.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,21 @@ where
266266
let tcx = self.tcx();
267267

268268
assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis);
269-
let field_ty =
270-
tcx.normalize_erasing_regions(self.elaborator.typing_env(), f.ty(tcx, args));
269+
// The type error for normalization may have been in dropck: see
270+
// `compute_drop_data` in rustc_borrowck, in which case we wouldn't have
271+
// deleted the MIR body and could have an error here as well.
272+
let field_ty = match tcx
273+
.try_normalize_erasing_regions(self.elaborator.typing_env(), f.ty(tcx, args))
274+
{
275+
Ok(t) => t,
276+
Err(_) => Ty::new_error(
277+
self.tcx(),
278+
self.elaborator
279+
.body()
280+
.tainted_by_errors
281+
.expect("Error in drop elaboration not found by dropck."),
282+
),
283+
};
271284

272285
(tcx.mk_place_field(base_place, field, field_ty), subpath)
273286
})

compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs

+44-25
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ use rustc_data_structures::fx::FxHashSet;
22
use rustc_infer::traits::query::type_op::DropckOutlives;
33
use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
44
use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
5-
use rustc_span::{DUMMY_SP, Span};
5+
use rustc_span::Span;
66
use tracing::{debug, instrument};
77

8+
use crate::solve::NextSolverError;
89
use crate::traits::query::NoSolution;
910
use crate::traits::query::normalize::QueryNormalizeExt;
10-
use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
11+
use crate::traits::{FromSolverError, Normalized, ObligationCause, ObligationCtxt};
1112

1213
/// This returns true if the type `ty` is "trivial" for
1314
/// dropck-outlives -- that is, if it doesn't require any types to
@@ -93,6 +94,20 @@ pub fn compute_dropck_outlives_inner<'tcx>(
9394
goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
9495
span: Span,
9596
) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
97+
match compute_dropck_outlives_with_errors(ocx, goal, span) {
98+
Ok(r) => Ok(r),
99+
Err(_) => Err(NoSolution),
100+
}
101+
}
102+
103+
pub fn compute_dropck_outlives_with_errors<'tcx, E>(
104+
ocx: &ObligationCtxt<'_, 'tcx, E>,
105+
goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
106+
span: Span,
107+
) -> Result<DropckOutlivesResult<'tcx>, Vec<E>>
108+
where
109+
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
110+
{
96111
let tcx = ocx.infcx.tcx;
97112
let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal;
98113

@@ -149,11 +164,11 @@ pub fn compute_dropck_outlives_inner<'tcx>(
149164
dtorck_constraint_for_ty_inner(
150165
tcx,
151166
ocx.infcx.typing_env(param_env),
152-
DUMMY_SP,
167+
span,
153168
depth,
154169
ty,
155170
&mut constraints,
156-
)?;
171+
);
157172

158173
// "outlives" represent types/regions that may be touched
159174
// by a destructor.
@@ -173,11 +188,20 @@ pub fn compute_dropck_outlives_inner<'tcx>(
173188
// do not themselves define a destructor", more or less. We have
174189
// to push them onto the stack to be expanded.
175190
for ty in constraints.dtorck_types.drain(..) {
176-
let Normalized { value: ty, obligations } =
177-
ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
178-
ocx.register_obligations(obligations);
191+
let ty = if let Ok(Normalized { value: ty, obligations }) =
192+
ocx.infcx.at(&cause, param_env).query_normalize(ty)
193+
{
194+
ocx.register_obligations(obligations);
195+
196+
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
197+
ty
198+
} else {
199+
ocx.deeply_normalize(&cause, param_env, ty)?;
179200

180-
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
201+
let errors = ocx.select_where_possible();
202+
debug!("normalize errors: {ty} ~> {errors:#?}");
203+
return Err(errors);
204+
};
181205

182206
match ty.kind() {
183207
// All parameters live for the duration of the
@@ -213,14 +237,14 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
213237
depth: usize,
214238
ty: Ty<'tcx>,
215239
constraints: &mut DropckConstraint<'tcx>,
216-
) -> Result<(), NoSolution> {
240+
) {
217241
if !tcx.recursion_limit().value_within_limit(depth) {
218242
constraints.overflows.push(ty);
219-
return Ok(());
243+
return;
220244
}
221245

222246
if trivial_dropck_outlives(tcx, ty) {
223-
return Ok(());
247+
return;
224248
}
225249

226250
match ty.kind() {
@@ -244,22 +268,20 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
244268
// single-element containers, behave like their element
245269
rustc_data_structures::stack::ensure_sufficient_stack(|| {
246270
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, *ety, constraints)
247-
})?;
271+
});
248272
}
249273

250274
ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
251275
for ty in tys.iter() {
252-
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints)?;
276+
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
253277
}
254-
Ok::<_, NoSolution>(())
255-
})?,
278+
}),
256279

257280
ty::Closure(_, args) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
258281
for ty in args.as_closure().upvar_tys() {
259-
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints)?;
282+
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
260283
}
261-
Ok::<_, NoSolution>(())
262-
})?,
284+
}),
263285

264286
ty::CoroutineClosure(_, args) => {
265287
rustc_data_structures::stack::ensure_sufficient_stack(|| {
@@ -271,10 +293,9 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
271293
depth + 1,
272294
ty,
273295
constraints,
274-
)?;
296+
);
275297
}
276-
Ok::<_, NoSolution>(())
277-
})?
298+
})
278299
}
279300

280301
ty::Coroutine(_, args) => {
@@ -313,7 +334,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
313334

314335
ty::Adt(def, args) => {
315336
let DropckConstraint { dtorck_types, outlives, overflows } =
316-
tcx.at(span).adt_dtorck_constraint(def.did())?;
337+
tcx.at(span).adt_dtorck_constraint(def.did());
317338
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
318339
// there, but that needs some way to handle cycles.
319340
constraints
@@ -346,9 +367,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
346367
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
347368
// By the time this code runs, all type variables ought to
348369
// be fully resolved.
349-
return Err(NoSolution);
370+
tcx.dcx().span_delayed_bug(span, format!("Unresolved type in dropck: {:?}.", ty));
350371
}
351372
}
352-
353-
Ok(())
354373
}

compiler/rustc_traits/src/dropck_outlives.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ fn dropck_outlives<'tcx>(
3030
}
3131

3232
/// Calculates the dtorck constraint for a type.
33-
pub(crate) fn adt_dtorck_constraint(
34-
tcx: TyCtxt<'_>,
35-
def_id: DefId,
36-
) -> Result<&DropckConstraint<'_>, NoSolution> {
33+
pub(crate) fn adt_dtorck_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &DropckConstraint<'_> {
3734
let def = tcx.adt_def(def_id);
3835
let span = tcx.def_span(def_id);
3936
let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id);
@@ -52,20 +49,20 @@ pub(crate) fn adt_dtorck_constraint(
5249
overflows: vec![],
5350
};
5451
debug!("dtorck_constraint: {:?} => {:?}", def, result);
55-
return Ok(tcx.arena.alloc(result));
52+
return tcx.arena.alloc(result);
5653
}
5754

5855
let mut result = DropckConstraint::empty();
5956
for field in def.all_fields() {
6057
let fty = tcx.type_of(field.did).instantiate_identity();
61-
dtorck_constraint_for_ty_inner(tcx, typing_env, span, 0, fty, &mut result)?;
58+
dtorck_constraint_for_ty_inner(tcx, typing_env, span, 0, fty, &mut result);
6259
}
6360
result.outlives.extend(tcx.destructor_constraints(def));
6461
dedup_dtorck_constraint(&mut result);
6562

6663
debug!("dtorck_constraint: {:?} => {:?}", def, result);
6764

68-
Ok(tcx.arena.alloc(result))
65+
tcx.arena.alloc(result)
6966
}
7067

7168
fn dedup_dtorck_constraint(c: &mut DropckConstraint<'_>) {

tests/crashes/103899.rs

-27
This file was deleted.

tests/crashes/105299.rs

-19
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Test that we don't ICE for a typeck error that only shows up in dropck
2+
// Version where the normalization error is an ambiguous trait implementation.
3+
// <[T] as ToOwned>::Owned is ambiguous on whether to use T: Clone or [T]::Clone.
4+
// Regression test for #105299
5+
6+
pub trait Foo: Clone {}
7+
8+
pub struct Bar<'a, T: Clone> {
9+
pub cow: std::borrow::Cow<'a, [T]>,
10+
11+
pub THIS_CAUSES_ICE: (),
12+
}
13+
14+
impl<T> Bar<'_, T>
15+
where
16+
T: Clone,
17+
[T]: Foo,
18+
{
19+
pub fn MOVES_SELF(self) {}
20+
//~^ ERROR type annotations needed
21+
}
22+
23+
pub fn main() {}

0 commit comments

Comments
 (0)