Rollup merge of #101433 - jackh726:better-static-placeholder-error, r=compiler-errors

Emit a note that static bounds from HRTBs are a bug

This note isn't perfect, but opening this to either 1) land as is or 2) get some feedback on how to improve it

Let r? `@compiler-errors` and cc. `@nikomatsakis`
This commit is contained in:
Dylan DPC 2022-09-14 19:26:18 +05:30 committed by GitHub
commit 430123164f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 543 additions and 173 deletions

View file

@ -22,6 +22,7 @@ use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;
use rustc_index::vec::IndexVec;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::relate::TypeRelation;
@ -129,7 +130,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let region_constraints = self.with_region_constraints(|region_constraints| {
make_query_region_constraints(
tcx,
region_obligations.iter().map(|r_o| (r_o.sup_type, r_o.sub_region)),
region_obligations
.iter()
.map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
region_constraints,
)
});
@ -248,6 +251,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
// the original values `v_o` that was canonicalized into a
// variable...
let constraint_category = cause.to_constraint_category();
for (index, original_value) in original_values.var_values.iter().enumerate() {
// ...with the value `v_r` of that variable from the query.
let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| {
@ -263,12 +268,14 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
if v_o != v_r {
output_query_region_constraints
.outlives
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
output_query_region_constraints
.outlives
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
output_query_region_constraints.outlives.push((
ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)),
constraint_category,
));
output_query_region_constraints.outlives.push((
ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)),
constraint_category,
));
}
}
@ -314,7 +321,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
// Screen out `'a: 'a` cases -- we skip the binder here but
// only compare the inner values to one another, so they are still at
// consistent binding levels.
let ty::OutlivesPredicate(k1, r2) = r_c.skip_binder();
let ty::OutlivesPredicate(k1, r2) = r_c.0.skip_binder();
if k1 != r2.into() { Some(r_c) } else { None }
}),
);
@ -559,7 +566,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Obligation<'tcx, ty::Predicate<'tcx>> {
let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder();
let ty::OutlivesPredicate(k1, r2) = predicate.0.skip_binder();
let atom = match k1.unpack() {
GenericArgKind::Lifetime(r1) => {
@ -574,7 +581,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
span_bug!(cause.span, "unexpected const outlives {:?}", predicate);
}
};
let predicate = predicate.rebind(atom).to_predicate(self.tcx);
let predicate = predicate.0.rebind(atom).to_predicate(self.tcx);
Obligation::new(cause, param_env, predicate)
}
@ -625,7 +632,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// creates query region constraints.
pub fn make_query_region_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>,
region_constraints: &RegionConstraintData<'tcx>,
) -> QueryRegionConstraints<'tcx> {
let RegionConstraintData { constraints, verifys, givens, member_constraints } =
@ -638,26 +645,31 @@ pub fn make_query_region_constraints<'tcx>(
let outlives: Vec<_> = constraints
.iter()
.map(|(k, _)| match *k {
// Swap regions because we are going from sub (<=) to outlives
// (>=).
Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
tcx.mk_region(ty::ReVar(v2)).into(),
tcx.mk_region(ty::ReVar(v1)),
),
Constraint::VarSubReg(v1, r2) => {
ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
}
Constraint::RegSubVar(r1, v2) => {
ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
}
Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
.map(|(k, origin)| {
// no bound vars in the code above
let constraint = ty::Binder::dummy(match *k {
// Swap regions because we are going from sub (<=) to outlives
// (>=).
Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
tcx.mk_region(ty::ReVar(v2)).into(),
tcx.mk_region(ty::ReVar(v1)),
),
Constraint::VarSubReg(v1, r2) => {
ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
}
Constraint::RegSubVar(r1, v2) => {
ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
}
Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
});
(constraint, origin.to_constraint_category())
})
.map(ty::Binder::dummy) // no bound vars in the code above
.chain(
outlives_obligations
.map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r))
.map(ty::Binder::dummy), // no bound vars in the code above
// no bound vars in the code above
.map(|(ty, r, constraint_category)| {
(ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), r)), constraint_category)
}),
)
.collect();

View file

@ -77,6 +77,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
self.note_region_origin(err, &parent);
}
infer::AscribeUserTypeProvePredicate(span) => {
RegionOriginNote::Plain {
span,
msg: fluent::infer::ascribe_user_type_prove_predicate,
}
.add_to_diagnostic(err);
}
}
}
@ -356,6 +363,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
err
}
infer::AscribeUserTypeProvePredicate(span) => {
let mut err =
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
note_and_explain_region(
self.tcx,
&mut err,
"lifetime instantiated with ",
sup,
"",
None,
);
note_and_explain_region(
self.tcx,
&mut err,
"but lifetime must outlive ",
sub,
"",
None,
);
err
}
}
}

View file

@ -20,6 +20,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::select;
use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
@ -408,7 +409,11 @@ pub enum SubregionOrigin<'tcx> {
/// Comparing the signature and requirements of an impl method against
/// the containing trait.
CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId },
CompareImplItemObligation {
span: Span,
impl_item_def_id: LocalDefId,
trait_item_def_id: DefId,
},
/// Checking that the bounds of a trait's associated type hold for a given impl
CheckAssociatedTypeBounds {
@ -416,12 +421,24 @@ pub enum SubregionOrigin<'tcx> {
impl_item_def_id: LocalDefId,
trait_item_def_id: DefId,
},
AscribeUserTypeProvePredicate(Span),
}
// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(SubregionOrigin<'_>, 32);
impl<'tcx> SubregionOrigin<'tcx> {
pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
match self {
Self::Subtype(type_trace) => type_trace.cause.to_constraint_category(),
Self::AscribeUserTypeProvePredicate(span) => ConstraintCategory::Predicate(*span),
_ => ConstraintCategory::BoringNoLocation,
}
}
}
/// Times when we replace late-bound regions with variables:
#[derive(Clone, Copy, Debug)]
pub enum LateBoundRegionConversionTime {
@ -1991,6 +2008,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
DataBorrowed(_, a) => a,
ReferenceOutlivesReferent(_, a) => a,
CompareImplItemObligation { span, .. } => span,
AscribeUserTypeProvePredicate(span) => span,
CheckAssociatedTypeBounds { ref parent, .. } => parent.span(),
}
}
@ -2023,6 +2041,10 @@ impl<'tcx> SubregionOrigin<'tcx> {
parent: Box::new(default()),
},
traits::ObligationCauseCode::AscribeUserTypeProvePredicate(span) => {
SubregionOrigin::AscribeUserTypeProvePredicate(span)
}
_ => default(),
}
}

View file

@ -69,6 +69,7 @@ use crate::infer::{
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
use smallvec::smallvec;
@ -163,7 +164,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let outlives =
&mut TypeOutlives::new(self, self.tcx, &region_bound_pairs, None, param_env);
outlives.type_must_outlive(origin, sup_type, sub_region);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
}
}
@ -207,6 +209,7 @@ pub trait TypeOutlivesDelegate<'tcx> {
origin: SubregionOrigin<'tcx>,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
constraint_category: ConstraintCategory<'tcx>,
);
fn push_verify(
@ -255,12 +258,13 @@ where
origin: infer::SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
region: ty::Region<'tcx>,
category: ConstraintCategory<'tcx>,
) {
assert!(!ty.has_escaping_bound_vars());
let mut components = smallvec![];
push_outlives_components(self.tcx, ty, &mut components);
self.components_must_outlive(origin, &components, region);
self.components_must_outlive(origin, &components, region, category);
}
fn components_must_outlive(
@ -268,12 +272,13 @@ where
origin: infer::SubregionOrigin<'tcx>,
components: &[Component<'tcx>],
region: ty::Region<'tcx>,
category: ConstraintCategory<'tcx>,
) {
for component in components.iter() {
let origin = origin.clone();
match component {
Component::Region(region1) => {
self.delegate.push_sub_region_constraint(origin, region, *region1);
self.delegate.push_sub_region_constraint(origin, region, *region1, category);
}
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
@ -282,7 +287,7 @@ where
self.projection_must_outlive(origin, region, *projection_ty);
}
Component::EscapingProjection(subcomponents) => {
self.components_must_outlive(origin, &subcomponents, region);
self.components_must_outlive(origin, &subcomponents, region, category);
}
Component::UnresolvedInferenceVariable(v) => {
// ignore this, we presume it will yield an error
@ -392,10 +397,20 @@ where
for k in projection_ty.substs {
match k.unpack() {
GenericArgKind::Lifetime(lt) => {
self.delegate.push_sub_region_constraint(origin.clone(), region, lt);
self.delegate.push_sub_region_constraint(
origin.clone(),
region,
lt,
origin.to_constraint_category(),
);
}
GenericArgKind::Type(ty) => {
self.type_must_outlive(origin.clone(), ty, region);
self.type_must_outlive(
origin.clone(),
ty,
region,
origin.to_constraint_category(),
);
}
GenericArgKind::Const(_) => {
// Const parameters don't impose constraints.
@ -433,7 +448,8 @@ where
let unique_bound = trait_bounds[0];
debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
debug!("projection_must_outlive: unique declared bound appears in trait ref");
self.delegate.push_sub_region_constraint(origin, region, unique_bound);
let category = origin.to_constraint_category();
self.delegate.push_sub_region_constraint(origin, region, unique_bound, category);
return;
}
@ -455,6 +471,7 @@ impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> {
origin: SubregionOrigin<'tcx>,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
_constraint_category: ConstraintCategory<'tcx>,
) {
self.sub_regions(origin, a, b)
}