Skip to content

lint incorrect implied bounds in wfcheck #109763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
lint implied_bounds_from_trait_impl
aliemjay committed May 5, 2023
commit f3cc860d82e22eb79f4d37b2f4d6c56fd173291e
37 changes: 35 additions & 2 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -113,10 +113,43 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
return;
}

let implied_bounds = infcx.implied_bounds_tys_v2(param_env, body_def_id, assumed_wf_types);
let infcx_v2 = infcx.fork();
let implied_bounds = infcx_v2.implied_bounds_tys_v2(param_env, &assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let errors_v2 = infcx_v2.resolve_regions(&outlives_env);
if errors_v2.is_empty() {
return;
}

let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let errors_v1 = infcx.resolve_regions(&outlives_env);
if !errors_v1.is_empty() {
infcx.err_ctxt().report_region_errors(body_def_id, &errors_v1);
return;
}

let _ = wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env);
let hir_id = tcx.hir().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 lint_level.is_error() {
infcx_v2.err_ctxt().report_region_errors(body_def_id, &errors_v2);
}
}

fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
42 changes: 42 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -3304,6 +3304,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,
@@ -4172,3 +4173,44 @@ declare_lint! {
Warn,
"\"invalid_parameter\" isn't a valid argument for `#[macro_export]`",
}

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 {
reference: "issue #109628 <https://github.com/rust-lang/rust/issues/109628>",
reason: FutureIncompatibilityReason::FutureReleaseError,
};
}
10 changes: 4 additions & 6 deletions compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Original file line number Diff line number Diff line change
@@ -30,8 +30,7 @@ pub trait InferCtxtExt<'a, 'tcx> {
fn implied_bounds_tys_v2(
&'a self,
param_env: ty::ParamEnv<'tcx>,
_body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> BoundsV2<'a, 'tcx>;
}

@@ -136,11 +135,10 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
fn implied_bounds_tys_v2(
&'a self,
param_env: ParamEnv<'tcx>,
_body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> BoundsV2<'a, 'tcx> {
tys.into_iter()
.flat_map(move |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() {
39 changes: 39 additions & 0 deletions tests/ui/implied-bounds/from-trait-impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
trait Trait {
type Assoc;
}

impl<X: 'static> Trait for (X,) {
type Assoc = ();
}

struct Foo<T: Trait>(T)
where
T::Assoc: Clone; // any predicate using `T::Assoc` works here

fn func1(foo: Foo<(&str,)>) {
//~^ WARN function is missing necessary lifetime bounds
//~| WARN this was previously accepted
let _: &'static str = foo.0.0;
}

#[deny(implied_bounds_from_trait_impl)]
fn func2(foo: Foo<(&str,)>) {
//~^ ERROR function is missing necessary lifetime bounds
//~| WARN this was previously accepted
//~| ERROR `&str` does not fulfill the required lifetime
let _: &'static str = foo.0.0;
}

trait TestTrait {}

impl<X> TestTrait for [Foo<(X,)>; 1] {}
//~^ WARN implementation is missing necessary lifetime bounds
//~| WARN this was previously accepted

#[deny(implied_bounds_from_trait_impl)]
impl<X> TestTrait for [Foo<(X,)>; 2] {}
//~^ ERROR implementation is missing necessary lifetime bounds
//~| WARN this was previously accepted
//~| ERROR `X` may not live long enough

fn main() {}
74 changes: 74 additions & 0 deletions tests/ui/implied-bounds/from-trait-impl.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
warning: function is missing necessary lifetime bounds
--> $DIR/from-trait-impl.rs:13:1
|
LL | fn func1(foo: Foo<(&str,)>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #109628 <https://github.com/rust-lang/rust/issues/109628>
= note: to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]`
= note: `#[warn(implied_bounds_from_trait_impl)]` on by default

error: function is missing necessary lifetime bounds
--> $DIR/from-trait-impl.rs:20:1
|
LL | fn func2(foo: Foo<(&str,)>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #109628 <https://github.com/rust-lang/rust/issues/109628>
= note: more concrete lifetime errors are emitted below
note: the lint level is defined here
--> $DIR/from-trait-impl.rs:19:8
|
LL | #[deny(implied_bounds_from_trait_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0477]: the type `&str` does not fulfill the required lifetime
--> $DIR/from-trait-impl.rs:20:15
|
LL | fn func2(foo: Foo<(&str,)>) {
| ^^^^^^^^^^^^
|
= note: type must satisfy the static lifetime

warning: implementation is missing necessary lifetime bounds
--> $DIR/from-trait-impl.rs:29:1
|
LL | impl<X> TestTrait for [Foo<(X,)>; 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #109628 <https://github.com/rust-lang/rust/issues/109628>
= note: to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]`

error: implementation is missing necessary lifetime bounds
--> $DIR/from-trait-impl.rs:34:1
|
LL | impl<X> TestTrait for [Foo<(X,)>; 2] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #109628 <https://github.com/rust-lang/rust/issues/109628>
= note: more concrete lifetime errors are emitted below
note: the lint level is defined here
--> $DIR/from-trait-impl.rs:33:8
|
LL | #[deny(implied_bounds_from_trait_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0310]: the parameter type `X` may not live long enough
--> $DIR/from-trait-impl.rs:34:23
|
LL | impl<X> TestTrait for [Foo<(X,)>; 2] {}
| ^^^^^^^^^^^^^^ ...so that the type `X` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | impl<X: 'static> TestTrait for [Foo<(X,)>; 2] {}
| +++++++++

error: aborting due to 4 previous errors; 2 warnings emitted

Some errors have detailed explanations: E0310, E0477.
For more information about an error, try `rustc --explain E0310`.