Skip to content

Commit

Permalink
Auto merge of rust-lang#118553 - jackh726:lint-implied-bounds, r=<try>
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
bors committed Dec 3, 2023
2 parents 1ca8b71 + b4e3421 commit a1ab930
Show file tree
Hide file tree
Showing 19 changed files with 413 additions and 45 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Expand Up @@ -356,7 +356,7 @@ fn check_opaque_meets_bounds<'tcx>(
// Can have different predicates to their defining use
hir::OpaqueTyOrigin::TyAlias { .. } => {
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, &wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Expand Up @@ -404,7 +404,7 @@ fn compare_method_predicate_entailment<'tcx>(
// lifetime parameters.
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() {
Expand Down Expand Up @@ -880,7 +880,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
// lifetime parameters.
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
);
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;

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

// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
let implied_bounds =
infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, &assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
}
Expand Down
Expand Up @@ -161,7 +161,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
}
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() {
Expand Down
58 changes: 54 additions & 4 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Expand Up @@ -13,6 +13,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::query::Providers;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
Expand Down Expand Up @@ -110,8 +111,6 @@ where

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

let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);

let errors = wfcx.select_all_or_error();
if !errors.is_empty() {
let err = infcx.err_ctxt().report_fulfillment_errors(errors);
Expand All @@ -126,9 +125,60 @@ where
}
}

let infcx_compat = infcx.fork();

debug!(?assumed_wf_types);
let implied_bounds = infcx.implied_bounds_tys(param_env, &assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);

wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?;
let errors = infcx.resolve_regions(&outlives_env);
if errors.is_empty() {
return Ok(());
}

let implied_bounds =
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let errors_compat = infcx_compat.resolve_regions(&outlives_env);
if !errors_compat.is_empty() {
return Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat));
}

// We don't want to emit this for dependents of Bevy, for now.
for ty in assumed_wf_types.iter() {
match ty.kind() {
ty::Adt(def, _) => {
let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did));
if adt_did.contains("ParamSet") {
return Ok(());
}
}
_ => {}
}
}

let hir_id = tcx.local_def_id_to_hir_id(body_def_id);
let (lint_level, _) = tcx
.lint_level_at_node(rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL, hir_id);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
hir_id,
tcx.def_span(body_def_id),
format!("{} is missing necessary lifetime bounds", tcx.def_descr(body_def_id.into())),
|lint| {
if !lint_level.is_error() {
lint.note(
"to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]`",
)
} else {
lint.note("more concrete lifetime errors are emitted below")
}
},
);
if true || lint_level.is_error() {
infcx.err_ctxt().report_region_errors(body_def_id, &errors);
}

infcx.tainted_by_errors().error_reported()
}

Expand Down Expand Up @@ -717,7 +767,7 @@ fn resolve_regions_with_wf_tys<'tcx>(
let infcx = tcx.infer_ctxt().build();
let outlives_environment = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
infcx.implied_bounds_tys_compat(param_env, id, wf_tys),
);
let region_bound_pairs = outlives_environment.region_bound_pairs();

Expand Down
Expand Up @@ -202,7 +202,8 @@ fn get_impl_args(
return Err(guar);
}

let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
let implied_bounds =
infcx.implied_bounds_tys_compat(param_env, impl1_def_id, &assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
Expand Down
42 changes: 42 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Expand Up @@ -3388,6 +3388,7 @@ declare_lint_pass! {
ILL_FORMED_ATTRIBUTE_INPUT,
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
IMPLIED_BOUNDS_ENTAILMENT,
IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
INCOMPLETE_INCLUDE,
INDIRECT_STRUCTURAL_MATCH,
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
Expand Down Expand Up @@ -4620,3 +4621,44 @@ declare_lint! {
reference: "issue #115010 <https://github.com/rust-lang/rust/issues/115010>",
};
}

declare_lint! {
/// The `implied_bounds_from_trait_impl` lint detects
/// a compiler bug allowed some code to assume invalid implied lifetime bounds.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(implied_bounds_from_trait_impl)]
///
/// trait Trait {
/// type Assoc;
/// }
///
/// impl<X: 'static> Trait for (X,) {
/// type Assoc = ();
/// }
///
/// struct Foo<T: Trait>(T)
/// where
/// T::Assoc: Clone; // any bound using `T::Assoc`
///
/// fn func(foo: Foo<(&str,)>) {
/// let _: &'static str = foo.0.0;
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// FIXME: explanataion
///
pub IMPLIED_BOUNDS_FROM_TRAIT_IMPL,
Warn,
"item uses illegal implied bounds derived from a trait impl",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
reference: "issue #109628 <https://github.com/rust-lang/rust/issues/109628>",
};
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/arena.rs
Expand Up @@ -53,7 +53,7 @@ macro_rules! arena_types {
rustc_middle::traits::query::NormalizationResult<'tcx>
>
>,
[] implied_outlives_bounds:
[] implied_outlives_bounds_compat:
rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx,
Vec<rustc_middle::traits::query::OutlivesBound<'tcx>>
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/erase.rs
Expand Up @@ -74,6 +74,10 @@ impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
}

impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> {
type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
}

impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
}
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_middle/src/query/mod.rs
Expand Up @@ -1953,7 +1953,7 @@ rustc_queries! {
desc { "normalizing `{}`", goal.value }
}

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

query implied_outlives_bounds(
goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
) -> Result<
&'tcx [OutlivesBound<'tcx>],
NoSolution,
> {
desc { "computing implied outlives bounds v2 for `{}`", goal.value }
}

/// Do not call this query directly:
/// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead.
query dropck_outlives(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/traits/query.rs
Expand Up @@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> {
/// case they are called implied bounds). They are fed to the
/// `OutlivesEnv` which in turn is supplied to the region checker and
/// other parts of the inference system.
#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/misc.rs
Expand Up @@ -191,10 +191,10 @@ pub fn all_fields_implement_trait<'tcx>(
// Check regions assuming the self type of the impl is WF
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(
infcx.implied_bounds_tys_compat(
param_env,
parent_cause.body_id,
FxIndexSet::from_iter([self_type]),
&FxIndexSet::from_iter([self_type]),
),
);
let errors = infcx.resolve_regions(&outlives_env);
Expand Down
44 changes: 35 additions & 9 deletions compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Expand Up @@ -9,20 +9,27 @@ use rustc_span::def_id::LocalDefId;

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

pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub trait InferCtxtExt<'a, 'tcx> {
fn implied_outlives_bounds(
fn implied_outlives_bounds_compat(
&self,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
ty: Ty<'tcx>,
) -> Vec<OutlivesBound<'tcx>>;

fn implied_bounds_tys(
fn implied_bounds_tys_compat(
&'a self,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> BoundsCompat<'a, 'tcx>;

fn implied_bounds_tys(
&'a self,
param_env: ty::ParamEnv<'tcx>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx>;
}

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

Expand Down Expand Up @@ -113,20 +120,39 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
if !errors.is_empty() {
self.tcx.sess.span_delayed_bug(
span,
"implied_outlives_bounds failed to solve obligations from instantiation",
"implied_outlives_bounds_compat failed to solve obligations from instantiation",
);
}
};

bounds
}

fn implied_bounds_tys(
fn implied_bounds_tys_compat(
&'a self,
param_env: ParamEnv<'tcx>,
body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> BoundsCompat<'a, 'tcx> {
tys.iter().flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, *ty))
}

fn implied_bounds_tys(
&'a self,
param_env: ParamEnv<'tcx>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx> {
tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty))
tys.iter()
.flat_map(move |&ty| {
let ty = self.resolve_vars_if_possible(ty);
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);

if ty.has_infer() {
return &[] as &[OutlivesBound<'_>];
}

self.tcx.implied_outlives_bounds(param_env.and(ty)).unwrap_or(&[])
})
.copied()
}
}

0 comments on commit a1ab930

Please sign in to comment.