Skip to content

Commit 1f34c1d

Browse files
committed
Auto merge of rust-lang#118553 - jackh726:lint-implied-bounds, r=<try>
lint incorrect implied bounds in wfcheck except for Bevy dependents Rebase of rust-lang#109763 Additionally, special cases Bevy `ParamSet` types to not trigger the lint. Opening for crater r? `@ghost`
2 parents d5fab33 + b4485be commit 1f34c1d

File tree

19 files changed

+413
-45
lines changed

19 files changed

+413
-45
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ fn check_opaque_meets_bounds<'tcx>(
356356
// Can have different predicates to their defining use
357357
hir::OpaqueTyOrigin::TyAlias { .. } => {
358358
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
359-
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
359+
let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, &wf_tys);
360360
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
361361
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
362362
}

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ fn compare_method_predicate_entailment<'tcx>(
404404
// lifetime parameters.
405405
let outlives_env = OutlivesEnvironment::with_bounds(
406406
param_env,
407-
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
407+
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
408408
);
409409
let errors = infcx.resolve_regions(&outlives_env);
410410
if !errors.is_empty() {
@@ -880,7 +880,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
880880
// lifetime parameters.
881881
let outlives_env = OutlivesEnvironment::with_bounds(
882882
param_env,
883-
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
883+
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
884884
);
885885
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
886886

@@ -2242,7 +2242,8 @@ pub(super) fn check_type_bounds<'tcx>(
22422242

22432243
// Finally, resolve all regions. This catches wily misuses of
22442244
// lifetime parameters.
2245-
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
2245+
let implied_bounds =
2246+
infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, &assumed_wf_types);
22462247
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
22472248
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
22482249
}

compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
161161
}
162162
let outlives_env = OutlivesEnvironment::with_bounds(
163163
param_env,
164-
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
164+
infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
165165
);
166166
let errors = infcx.resolve_regions(&outlives_env);
167167
if !errors.is_empty() {

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+54-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives;
1313
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
1414
use rustc_middle::mir::ConstraintCategory;
1515
use rustc_middle::query::Providers;
16+
use rustc_middle::ty::print::with_no_trimmed_paths;
1617
use rustc_middle::ty::trait_def::TraitSpecializationKind;
1718
use rustc_middle::ty::{
1819
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
@@ -110,8 +111,6 @@ where
110111

111112
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
112113

113-
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
114-
115114
let errors = wfcx.select_all_or_error();
116115
if !errors.is_empty() {
117116
let err = infcx.err_ctxt().report_fulfillment_errors(errors);
@@ -126,9 +125,60 @@ where
126125
}
127126
}
128127

128+
let infcx_compat = infcx.fork();
129+
130+
debug!(?assumed_wf_types);
131+
let implied_bounds = infcx.implied_bounds_tys(param_env, &assumed_wf_types);
129132
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
130133

131-
wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?;
134+
let errors = infcx.resolve_regions(&outlives_env);
135+
if errors.is_empty() {
136+
return Ok(());
137+
}
138+
139+
let implied_bounds =
140+
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types);
141+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
142+
let errors_compat = infcx_compat.resolve_regions(&outlives_env);
143+
if !errors_compat.is_empty() {
144+
return Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat));
145+
}
146+
147+
// We don't want to emit this for dependents of Bevy, for now.
148+
for ty in assumed_wf_types.iter() {
149+
match ty.kind() {
150+
ty::Adt(def, _) => {
151+
let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did));
152+
if adt_did == "bevy_ecs::system::ParamSet" {
153+
return Ok(());
154+
}
155+
}
156+
_ => {}
157+
}
158+
}
159+
160+
let hir_id = tcx.local_def_id_to_hir_id(body_def_id);
161+
let (lint_level, _) = tcx
162+
.lint_level_at_node(rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL, hir_id);
163+
tcx.struct_span_lint_hir(
164+
rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
165+
hir_id,
166+
tcx.def_span(body_def_id),
167+
format!("{} is missing necessary lifetime bounds", tcx.def_descr(body_def_id.into())),
168+
|lint| {
169+
if !lint_level.is_error() {
170+
lint.note(
171+
"to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]`",
172+
)
173+
} else {
174+
lint.note("more concrete lifetime errors are emitted below")
175+
}
176+
},
177+
);
178+
if true || lint_level.is_error() {
179+
infcx.err_ctxt().report_region_errors(body_def_id, &errors);
180+
}
181+
132182
infcx.tainted_by_errors().error_reported()
133183
}
134184

@@ -717,7 +767,7 @@ fn resolve_regions_with_wf_tys<'tcx>(
717767
let infcx = tcx.infer_ctxt().build();
718768
let outlives_environment = OutlivesEnvironment::with_bounds(
719769
param_env,
720-
infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
770+
infcx.implied_bounds_tys_compat(param_env, id, wf_tys),
721771
);
722772
let region_bound_pairs = outlives_environment.region_bound_pairs();
723773

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ fn get_impl_args(
202202
return Err(guar);
203203
}
204204

205-
let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
205+
let implied_bounds =
206+
infcx.implied_bounds_tys_compat(param_env, impl1_def_id, &assumed_wf_types);
206207
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
207208
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
208209
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {

compiler/rustc_lint_defs/src/builtin.rs

+42
Original file line numberDiff line numberDiff line change
@@ -3388,6 +3388,7 @@ declare_lint_pass! {
33883388
ILL_FORMED_ATTRIBUTE_INPUT,
33893389
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
33903390
IMPLIED_BOUNDS_ENTAILMENT,
3391+
IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
33913392
INCOMPLETE_INCLUDE,
33923393
INDIRECT_STRUCTURAL_MATCH,
33933394
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
@@ -4620,3 +4621,44 @@ declare_lint! {
46204621
reference: "issue #115010 <https://github.com/rust-lang/rust/issues/115010>",
46214622
};
46224623
}
4624+
4625+
declare_lint! {
4626+
/// The `implied_bounds_from_trait_impl` lint detects
4627+
/// a compiler bug allowed some code to assume invalid implied lifetime bounds.
4628+
///
4629+
/// ### Example
4630+
///
4631+
/// ```rust,compile_fail
4632+
/// #![deny(implied_bounds_from_trait_impl)]
4633+
///
4634+
/// trait Trait {
4635+
/// type Assoc;
4636+
/// }
4637+
///
4638+
/// impl<X: 'static> Trait for (X,) {
4639+
/// type Assoc = ();
4640+
/// }
4641+
///
4642+
/// struct Foo<T: Trait>(T)
4643+
/// where
4644+
/// T::Assoc: Clone; // any bound using `T::Assoc`
4645+
///
4646+
/// fn func(foo: Foo<(&str,)>) {
4647+
/// let _: &'static str = foo.0.0;
4648+
/// }
4649+
/// ```
4650+
///
4651+
/// {{produces}}
4652+
///
4653+
/// ### Explanation
4654+
///
4655+
/// FIXME: explanataion
4656+
///
4657+
pub IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
4658+
Warn,
4659+
"item uses illegal implied bounds derived from a trait impl",
4660+
@future_incompatible = FutureIncompatibleInfo {
4661+
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
4662+
reference: "issue #109628 <https://github.com/rust-lang/rust/issues/109628>",
4663+
};
4664+
}

compiler/rustc_middle/src/arena.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ macro_rules! arena_types {
5353
rustc_middle::traits::query::NormalizationResult<'tcx>
5454
>
5555
>,
56-
[] implied_outlives_bounds:
56+
[] implied_outlives_bounds_compat:
5757
rustc_middle::infer::canonical::Canonical<'tcx,
5858
rustc_middle::infer::canonical::QueryResponse<'tcx,
5959
Vec<rustc_middle::traits::query::OutlivesBound<'tcx>>

compiler/rustc_middle/src/query/erase.rs

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
7474
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
7575
}
7676

77+
impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> {
78+
type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
79+
}
80+
7781
impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
7882
type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
7983
}

compiler/rustc_middle/src/query/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,7 @@ rustc_queries! {
19531953
desc { "normalizing `{}`", goal.value }
19541954
}
19551955

1956-
query implied_outlives_bounds(
1956+
query implied_outlives_bounds_compat(
19571957
goal: CanonicalTyGoal<'tcx>
19581958
) -> Result<
19591959
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
@@ -1962,6 +1962,15 @@ rustc_queries! {
19621962
desc { "computing implied outlives bounds for `{}`", goal.value.value }
19631963
}
19641964

1965+
query implied_outlives_bounds(
1966+
goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1967+
) -> Result<
1968+
&'tcx [OutlivesBound<'tcx>],
1969+
NoSolution,
1970+
> {
1971+
desc { "computing implied outlives bounds v2 for `{}`", goal.value }
1972+
}
1973+
19651974
/// Do not call this query directly:
19661975
/// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead.
19671976
query dropck_outlives(

compiler/rustc_middle/src/traits/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> {
191191
/// case they are called implied bounds). They are fed to the
192192
/// `OutlivesEnv` which in turn is supplied to the region checker and
193193
/// other parts of the inference system.
194-
#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
194+
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
195195
pub enum OutlivesBound<'tcx> {
196196
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
197197
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),

compiler/rustc_trait_selection/src/traits/misc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,10 @@ pub fn all_fields_implement_trait<'tcx>(
191191
// Check regions assuming the self type of the impl is WF
192192
let outlives_env = OutlivesEnvironment::with_bounds(
193193
param_env,
194-
infcx.implied_bounds_tys(
194+
infcx.implied_bounds_tys_compat(
195195
param_env,
196196
parent_cause.body_id,
197-
FxIndexSet::from_iter([self_type]),
197+
&FxIndexSet::from_iter([self_type]),
198198
),
199199
);
200200
let errors = infcx.resolve_regions(&outlives_env);

compiler/rustc_trait_selection/src/traits/outlives_bounds.rs

+35-9
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,27 @@ use rustc_span::def_id::LocalDefId;
99

1010
pub use rustc_middle::traits::query::OutlivesBound;
1111

12+
pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
1213
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
1314
pub trait InferCtxtExt<'a, 'tcx> {
14-
fn implied_outlives_bounds(
15+
fn implied_outlives_bounds_compat(
1516
&self,
1617
param_env: ty::ParamEnv<'tcx>,
1718
body_id: LocalDefId,
1819
ty: Ty<'tcx>,
1920
) -> Vec<OutlivesBound<'tcx>>;
2021

21-
fn implied_bounds_tys(
22+
fn implied_bounds_tys_compat(
2223
&'a self,
2324
param_env: ty::ParamEnv<'tcx>,
2425
body_id: LocalDefId,
25-
tys: FxIndexSet<Ty<'tcx>>,
26+
tys: &'a FxIndexSet<Ty<'tcx>>,
27+
) -> BoundsCompat<'a, 'tcx>;
28+
29+
fn implied_bounds_tys(
30+
&'a self,
31+
param_env: ty::ParamEnv<'tcx>,
32+
tys: &'a FxIndexSet<Ty<'tcx>>,
2633
) -> Bounds<'a, 'tcx>;
2734
}
2835

@@ -47,7 +54,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
4754
/// into the inference context with this body-id.
4855
/// - `ty`, the type that we are supposed to assume is WF.
4956
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
50-
fn implied_outlives_bounds(
57+
fn implied_outlives_bounds_compat(
5158
&self,
5259
param_env: ty::ParamEnv<'tcx>,
5360
body_id: LocalDefId,
@@ -67,7 +74,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
6774
let mut canonical_var_values = OriginalQueryValues::default();
6875
let canonical_ty =
6976
self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
70-
let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
77+
let Ok(canonical_result) = self.tcx.implied_outlives_bounds_compat(canonical_ty) else {
7178
return vec![];
7279
};
7380

@@ -113,20 +120,39 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
113120
if !errors.is_empty() {
114121
self.tcx.sess.span_delayed_bug(
115122
span,
116-
"implied_outlives_bounds failed to solve obligations from instantiation",
123+
"implied_outlives_bounds_compat failed to solve obligations from instantiation",
117124
);
118125
}
119126
};
120127

121128
bounds
122129
}
123130

124-
fn implied_bounds_tys(
131+
fn implied_bounds_tys_compat(
125132
&'a self,
126133
param_env: ParamEnv<'tcx>,
127134
body_id: LocalDefId,
128-
tys: FxIndexSet<Ty<'tcx>>,
135+
tys: &'a FxIndexSet<Ty<'tcx>>,
136+
) -> BoundsCompat<'a, 'tcx> {
137+
tys.iter().flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, *ty))
138+
}
139+
140+
fn implied_bounds_tys(
141+
&'a self,
142+
param_env: ParamEnv<'tcx>,
143+
tys: &'a FxIndexSet<Ty<'tcx>>,
129144
) -> Bounds<'a, 'tcx> {
130-
tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty))
145+
tys.iter()
146+
.flat_map(move |&ty| {
147+
let ty = self.resolve_vars_if_possible(ty);
148+
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
149+
150+
if ty.has_infer() {
151+
return &[] as &[OutlivesBound<'_>];
152+
}
153+
154+
self.tcx.implied_outlives_bounds(param_env.and(ty)).unwrap_or(&[])
155+
})
156+
.copied()
131157
}
132158
}

0 commit comments

Comments
 (0)