Skip to content

Commit 40f1f75

Browse files
committed
Auto merge of rust-lang#136539 - matthewjasper:late-normalize-errors, r=<try>
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 7b31983 + abfa754 commit 40f1f75

File tree

12 files changed

+228
-30
lines changed

12 files changed

+228
-30
lines changed

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

+42-13
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ 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};
77
use rustc_middle::traits::query::DropckOutlivesResult;
88
use rustc_middle::ty::relate::Relate;
99
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
1010
use rustc_mir_dataflow::ResultsCursor;
1111
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
1212
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
1313
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
14-
use rustc_span::DUMMY_SP;
14+
use rustc_span::{DUMMY_SP, Span};
15+
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
16+
use rustc_trait_selection::traits::ObligationCtxt;
17+
use rustc_trait_selection::traits::query::dropck_outlives;
1518
use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
1619
use tracing::debug;
1720

@@ -162,9 +165,10 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
162165
fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
163166
for local in boring_locals {
164167
let local_ty = self.cx.body.local_decls[local].ty;
168+
let local_span = self.cx.body.local_decls[local].source_info.span;
165169
let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
166170
let typeck = &self.cx.typeck;
167-
move || LivenessContext::compute_drop_data(typeck, local_ty)
171+
move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
168172
});
169173

170174
drop_data.dropck_result.report_overflows(
@@ -522,9 +526,10 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
522526
values::pretty_print_points(self.location_map, live_at.iter()),
523527
);
524528

529+
let local_span = self.body.local_decls()[dropped_local].source_info.span;
525530
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
526531
let typeck = &self.typeck;
527-
move || Self::compute_drop_data(typeck, dropped_ty)
532+
move || Self::compute_drop_data(typeck, dropped_ty, local_span)
528533
});
529534

530535
if let Some(data) = &drop_data.region_constraint_data {
@@ -589,19 +594,43 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
589594
}
590595
}
591596

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

595-
match typeck
596-
.infcx
597-
.param_env
598-
.and(DropckOutlives { dropped_ty })
599-
.fully_perform(typeck.infcx, DUMMY_SP)
600-
{
606+
match op.fully_perform(typeck.infcx, DUMMY_SP) {
601607
Ok(TypeOpOutput { output, constraints, .. }) => {
602608
DropData { dropck_result: output, region_constraint_data: constraints }
603609
}
604-
Err(_) => DropData { dropck_result: Default::default(), region_constraint_data: None },
610+
Err(_) => {
611+
// The `ErrorGuaranteed` from `fully_perform` comes from delay_span_bug,
612+
// so emitting an error here is not redundant.
613+
let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx);
614+
let errors = match dropck_outlives::compute_dropck_outlives_with_errors(
615+
&ocx, op, span, true,
616+
) {
617+
Ok(_) => ocx.select_all_or_error(),
618+
Err(e) => {
619+
if e.is_empty() {
620+
ocx.select_all_or_error()
621+
} else {
622+
e
623+
}
624+
}
625+
};
626+
627+
if !errors.is_empty() {
628+
typeck.infcx.err_ctxt().report_fulfillment_errors(errors);
629+
} else {
630+
rustc_middle::span_bug!(span, "Rerunning drop data query produced no error.");
631+
}
632+
DropData { dropck_result: Default::default(), region_constraint_data: None }
633+
}
605634
}
606635
}
607636
}

compiler/rustc_mir_dataflow/src/elaborate_drops.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,21 @@ where
274274
let tcx = self.tcx();
275275

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

280293
(tcx.mk_place_field(base_place, field, field_ty), subpath)
281294
})

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

+42-9
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,21 @@ 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, false) {
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+
report_errors: bool,
108+
) -> Result<DropckOutlivesResult<'tcx>, Vec<E>>
109+
where
110+
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
111+
{
96112
let tcx = ocx.infcx.tcx;
97113
let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal;
98114

@@ -146,14 +162,17 @@ pub fn compute_dropck_outlives_inner<'tcx>(
146162
result.overflows.len(),
147163
ty_stack.len()
148164
);
149-
dtorck_constraint_for_ty_inner(
165+
match dtorck_constraint_for_ty_inner(
150166
tcx,
151167
ocx.infcx.typing_env(param_env),
152-
DUMMY_SP,
168+
span,
153169
depth,
154170
ty,
155171
&mut constraints,
156-
)?;
172+
) {
173+
Err(_) => return Err(Vec::new()),
174+
_ => (),
175+
};
157176

158177
// "outlives" represent types/regions that may be touched
159178
// by a destructor.
@@ -173,11 +192,25 @@ pub fn compute_dropck_outlives_inner<'tcx>(
173192
// do not themselves define a destructor", more or less. We have
174193
// to push them onto the stack to be expanded.
175194
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);
195+
let ty = if report_errors {
196+
let normalized_ty = ocx.deeply_normalize(&cause, param_env, ty)?;
179197

180-
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
198+
let errors = ocx.select_where_possible();
199+
if !errors.is_empty() {
200+
debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}");
201+
return Err(errors);
202+
}
203+
normalized_ty
204+
} else if let Ok(Normalized { value: ty, obligations }) =
205+
ocx.infcx.at(&cause, param_env).query_normalize(ty)
206+
{
207+
ocx.register_obligations(obligations);
208+
209+
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
210+
ty
211+
} else {
212+
return Err(Vec::new());
213+
};
181214

182215
match ty.kind() {
183216
// All parameters live for the duration of the
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Test that we don't ICE for a typeck error that only shows up in dropck
2+
// issue #135039
3+
//@ edition:2018
4+
5+
pub trait AuthUser {
6+
type Id;
7+
}
8+
9+
pub trait AuthnBackend {
10+
type User: AuthUser;
11+
}
12+
13+
pub struct AuthSession<Backend: AuthnBackend> {
14+
user: Option<Backend::User>,
15+
data: Option<<<Backend as AuthnBackend>::User as AuthUser>::Id>,
16+
}
17+
18+
pub trait Authz: Sized {
19+
type AuthnBackend: AuthnBackend<User = Self>;
20+
}
21+
22+
pub trait Query<User: Authz> {
23+
type Output;
24+
async fn run(&self) -> Result<Self::Output, ()>;
25+
}
26+
27+
pub async fn run_query<User: Authz, Q: Query<User> + 'static>(
28+
auth: AuthSession<User::AuthnBackend>,
29+
//~^ ERROR the trait bound `User: AuthUser` is not satisfied [E0277]
30+
//~| ERROR the trait bound `User: AuthUser` is not satisfied [E0277]
31+
query: Q,
32+
) -> Result<Q::Output, ()> {
33+
let user = auth.user;
34+
query.run().await
35+
}
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `User: AuthUser` is not satisfied
2+
--> $DIR/dropck-only-error-async.rs:28:5
3+
|
4+
LL | auth: AuthSession<User::AuthnBackend>,
5+
| ^^^^ the trait `AuthUser` is not implemented for `User`
6+
7+
error[E0277]: the trait bound `User: AuthUser` is not satisfied
8+
--> $DIR/dropck-only-error-async.rs:28:5
9+
|
10+
LL | auth: AuthSession<User::AuthnBackend>,
11+
| ^^^^ the trait `AuthUser` is not implemented for `User`
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0277`.

tests/ui/dropck/dropck-only-error.rs

+23
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+
// issue #135039
3+
4+
pub trait AuthUser {
5+
type Id;
6+
}
7+
8+
pub trait AuthnBackend {
9+
type User: AuthUser;
10+
}
11+
12+
pub struct AuthSession<Backend: AuthnBackend> {
13+
data: Option<<<Backend as AuthnBackend>::User as AuthUser>::Id>,
14+
}
15+
16+
pub trait Authz: Sized {
17+
type AuthnBackend: AuthnBackend<User = Self>;
18+
}
19+
20+
pub fn run_query<User: Authz>(auth: AuthSession<User::AuthnBackend>) {}
21+
//~^ ERROR the trait bound `User: AuthUser` is not satisfied [E0277]
22+
23+
fn main() {}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0277]: the trait bound `User: AuthUser` is not satisfied
2+
--> $DIR/dropck-only-error.rs:20:31
3+
|
4+
LL | pub fn run_query<User: Authz>(auth: AuthSession<User::AuthnBackend>) {}
5+
| ^^^^ the trait `AuthUser` is not implemented for `User`
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `(): BaseWithAssoc` is not satisfied
2+
--> $DIR/issue-103899.rs:24:54
3+
|
4+
LL | fn trigger<L: WrapperWithAssoc<BaseAssoc = ()>>() -> DoubleProject<L> {
5+
| ^^^^^^^^^^^^^^^^ the trait `BaseWithAssoc` is not implemented for `()`
6+
|
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/issue-103899.rs:4:1
9+
|
10+
LL | trait BaseWithAssoc {
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `(): BaseWithAssoc` is not satisfied
2+
--> $DIR/issue-103899.rs:24:54
3+
|
4+
LL | fn trigger<L: WrapperWithAssoc<BaseAssoc = ()>>() -> DoubleProject<L> {
5+
| ^^^^^^^^^^^^^^^^ the trait `BaseWithAssoc` is not implemented for `()`
6+
|
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/issue-103899.rs:4:1
9+
|
10+
LL | trait BaseWithAssoc {
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.

tests/ui/typeck/issue-103899.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
//@ revisions: current next
22
//@ ignore-compare-mode-next-solver (explicit revisions)
3-
//@ check-fail
4-
//@ failure-status: 101
5-
//@ dont-check-compiler-stderr
6-
//@ known-bug: #103899
73

84
trait BaseWithAssoc {
95
type Assoc;
@@ -26,6 +22,7 @@ struct DoubleProject<L: WrapperWithAssoc> {
2622
}
2723

2824
fn trigger<L: WrapperWithAssoc<BaseAssoc = ()>>() -> DoubleProject<L> {
25+
//~^ ERROR the trait bound `(): BaseWithAssoc` is not satisfied [E0277]
2926
loop {}
3027
}
3128

tests/ui/wf/hir-wf-check-erase-regions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ impl<'a, T, const N: usize> IntoIterator for &'a Table<T, N> {
88
//~^ ERROR `&'a T` is not an iterator
99
type Item = &'a T;
1010

11-
fn into_iter(self) -> Self::IntoIter { //~ ERROR `&'a T` is not an iterator
11+
fn into_iter(self) -> Self::IntoIter {
12+
//~^ ERROR `&'a T` is not an iterator
13+
//~| ERROR `&T` is not an iterator
1214
unimplemented!()
1315
}
1416
}

tests/ui/wf/hir-wf-check-erase-regions.stderr

+11-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ LL | fn into_iter(self) -> Self::IntoIter {
3434
note: required by a bound in `Flatten`
3535
--> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
3636

37-
error: aborting due to 3 previous errors
37+
error[E0277]: `&T` is not an iterator
38+
--> $DIR/hir-wf-check-erase-regions.rs:11:27
39+
|
40+
LL | fn into_iter(self) -> Self::IntoIter {
41+
| ^^^^^^^^^^^^^^ `&T` is not an iterator
42+
|
43+
= help: the trait `Iterator` is not implemented for `&T`
44+
= help: the trait `Iterator` is implemented for `&mut I`
45+
= note: required for `&T` to implement `IntoIterator`
46+
47+
error: aborting due to 4 previous errors
3848

3949
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)