Skip to content

Commit 35b6491

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 a9e7b30 + defefbc commit 35b6491

18 files changed

+255
-85
lines changed

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

+44-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,45 @@ 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+
// We don't run dropck on HIR, and dropck looks inside fields of
612+
// types, so there's no guarantee that it succeeds. We also
613+
// can't rely on the the `ErrorGuaranteed` from `fully_perform` here
614+
// because it comes from delay_span_bug.
615+
let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx);
616+
let errors = match dropck_outlives::compute_dropck_outlives_with_errors(
617+
&ocx, op, span, true,
618+
) {
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+
rustc_middle::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_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

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() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0284]: type annotations needed
2+
--> $DIR/dropck-only-error-ambiguity.rs:19:23
3+
|
4+
LL | pub fn MOVES_SELF(self) {}
5+
| ^^^^ cannot infer type
6+
|
7+
= note: cannot satisfy `<[T] as ToOwned>::Owned == _`
8+
9+
error: aborting due to 1 previous error
10+
11+
For more information about this error, try `rustc --explain E0284`.

tests/crashes/135039.rs renamed to tests/ui/dropck/dropck-only-error-async.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
//@ known-bug: #135039
2-
//@ edition:2021
3-
4-
pub type UserId<Backend> = <<Backend as AuthnBackend>::User as AuthUser>::Id;
1+
// Test that we don't ICE for a typeck error that only shows up in dropck
2+
// issue #135039
3+
//@ edition:2018
54

65
pub trait AuthUser {
76
type Id;
@@ -13,7 +12,7 @@ pub trait AuthnBackend {
1312

1413
pub struct AuthSession<Backend: AuthnBackend> {
1514
user: Option<Backend::User>,
16-
data: Option<UserId<Backend>>,
15+
data: Option<<<Backend as AuthnBackend>::User as AuthUser>::Id>,
1716
}
1817

1918
pub trait Authz: Sized {
@@ -27,8 +26,12 @@ pub trait Query<User: Authz> {
2726

2827
pub async fn run_query<User: Authz, Q: Query<User> + 'static>(
2928
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]
3031
query: Q,
3132
) -> Result<Q::Output, ()> {
3233
let user = auth.user;
3334
query.run().await
3435
}
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/crashes/91985.rs renamed to tests/ui/dropck/dropck-only-error-gat.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//@ known-bug: #91985
2-
3-
#![feature(generic_associated_types)]
1+
// Test that we don't ICE for a typeck error that only shows up in dropck
2+
// Version that uses a generic associated type
3+
// Regression test for #91985
44

55
pub trait Trait1 {
66
type Associated: Ord;
@@ -22,7 +22,7 @@ impl GatTrait for GatStruct {
2222

2323
pub struct OuterStruct<T1: Trait1, T2: Trait2> {
2424
inner: InnerStruct<T2, GatStruct>,
25-
t1: T1,
25+
t1: T1,
2626
}
2727

2828
pub struct InnerStruct<T: Trait2, G: GatTrait> {
@@ -35,6 +35,7 @@ where
3535
T2: Trait2<Associated = T1::Associated>,
3636
{
3737
pub fn new() -> Self {
38+
//~^ ERROR the trait bound `<T1 as Trait1>::Associated: Clone` is not satisfied
3839
todo!()
3940
}
4041
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `<T1 as Trait1>::Associated: Clone` is not satisfied
2+
--> $DIR/dropck-only-error-gat.rs:37:21
3+
|
4+
LL | pub fn new() -> Self {
5+
| ^^^^ the trait `Clone` is not implemented for `<T1 as Trait1>::Associated`
6+
|
7+
note: required by a bound in `GatTrait::Gat`
8+
--> $DIR/dropck-only-error-gat.rs:14:17
9+
|
10+
LL | type Gat<T: Clone>;
11+
| ^^^^^ required by this bound in `GatTrait::Gat`
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)