Auto merge of #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 #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 #103899 Closes #135039 r? `@compiler-errors` (feel free to re-assign)
This commit is contained in:
commit
ed49386d3a
20 changed files with 262 additions and 109 deletions
|
@ -2,12 +2,13 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_infer::traits::query::type_op::DropckOutlives;
|
||||
use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
|
||||
use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_span::Span;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::solve::NextSolverError;
|
||||
use crate::traits::query::NoSolution;
|
||||
use crate::traits::query::normalize::QueryNormalizeExt;
|
||||
use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
|
||||
use crate::traits::{FromSolverError, Normalized, ObligationCause, ObligationCtxt};
|
||||
|
||||
/// This returns true if the type `ty` is "trivial" for
|
||||
/// dropck-outlives -- that is, if it doesn't require any types to
|
||||
|
@ -93,6 +94,20 @@ pub fn compute_dropck_outlives_inner<'tcx>(
|
|||
goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
|
||||
span: Span,
|
||||
) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
|
||||
match compute_dropck_outlives_with_errors(ocx, goal, span) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(NoSolution),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_dropck_outlives_with_errors<'tcx, E>(
|
||||
ocx: &ObligationCtxt<'_, 'tcx, E>,
|
||||
goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
|
||||
span: Span,
|
||||
) -> Result<DropckOutlivesResult<'tcx>, Vec<E>>
|
||||
where
|
||||
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
|
||||
{
|
||||
let tcx = ocx.infcx.tcx;
|
||||
let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal;
|
||||
|
||||
|
@ -149,11 +164,11 @@ pub fn compute_dropck_outlives_inner<'tcx>(
|
|||
dtorck_constraint_for_ty_inner(
|
||||
tcx,
|
||||
ocx.infcx.typing_env(param_env),
|
||||
DUMMY_SP,
|
||||
span,
|
||||
depth,
|
||||
ty,
|
||||
&mut constraints,
|
||||
)?;
|
||||
);
|
||||
|
||||
// "outlives" represent types/regions that may be touched
|
||||
// by a destructor.
|
||||
|
@ -173,11 +188,20 @@ pub fn compute_dropck_outlives_inner<'tcx>(
|
|||
// do not themselves define a destructor", more or less. We have
|
||||
// to push them onto the stack to be expanded.
|
||||
for ty in constraints.dtorck_types.drain(..) {
|
||||
let Normalized { value: ty, obligations } =
|
||||
ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
|
||||
ocx.register_obligations(obligations);
|
||||
let ty = if let Ok(Normalized { value: ty, obligations }) =
|
||||
ocx.infcx.at(&cause, param_env).query_normalize(ty)
|
||||
{
|
||||
ocx.register_obligations(obligations);
|
||||
|
||||
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
|
||||
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
|
||||
ty
|
||||
} else {
|
||||
ocx.deeply_normalize(&cause, param_env, ty)?;
|
||||
|
||||
let errors = ocx.select_where_possible();
|
||||
debug!("normalize errors: {ty} ~> {errors:#?}");
|
||||
return Err(errors);
|
||||
};
|
||||
|
||||
match ty.kind() {
|
||||
// All parameters live for the duration of the
|
||||
|
@ -213,14 +237,14 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||
depth: usize,
|
||||
ty: Ty<'tcx>,
|
||||
constraints: &mut DropckConstraint<'tcx>,
|
||||
) -> Result<(), NoSolution> {
|
||||
) {
|
||||
if !tcx.recursion_limit().value_within_limit(depth) {
|
||||
constraints.overflows.push(ty);
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
if trivial_dropck_outlives(tcx, ty) {
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
|
@ -244,22 +268,20 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||
// single-element containers, behave like their element
|
||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, *ety, constraints)
|
||||
})?;
|
||||
});
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||
for ty in tys.iter() {
|
||||
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints)?;
|
||||
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
|
||||
}
|
||||
Ok::<_, NoSolution>(())
|
||||
})?,
|
||||
}),
|
||||
|
||||
ty::Closure(_, args) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||
for ty in args.as_closure().upvar_tys() {
|
||||
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints)?;
|
||||
dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
|
||||
}
|
||||
Ok::<_, NoSolution>(())
|
||||
})?,
|
||||
}),
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||
|
@ -271,10 +293,9 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||
depth + 1,
|
||||
ty,
|
||||
constraints,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
Ok::<_, NoSolution>(())
|
||||
})?
|
||||
})
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
|
@ -313,7 +334,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||
|
||||
ty::Adt(def, args) => {
|
||||
let DropckConstraint { dtorck_types, outlives, overflows } =
|
||||
tcx.at(span).adt_dtorck_constraint(def.did())?;
|
||||
tcx.at(span).adt_dtorck_constraint(def.did());
|
||||
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
|
||||
// there, but that needs some way to handle cycles.
|
||||
constraints
|
||||
|
@ -346,9 +367,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
|
||||
// By the time this code runs, all type variables ought to
|
||||
// be fully resolved.
|
||||
return Err(NoSolution);
|
||||
tcx.dcx().span_delayed_bug(span, format!("Unresolved type in dropck: {:?}.", ty));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue