Skip to content

Commit

Permalink
Report a specialized error when a 'static obligation comes from an …
Browse files Browse the repository at this point in the history
…`impl dyn Trait`

```text
error: lifetime may not live long enough
  --> $DIR/static-impl-obligation.rs:8:27
   |
LL |     fn bar<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
LL |         let y: &dyn Foo = x;
   |                           ^ cast requires that `'a` must outlive `'static`
LL |         y.hello();
   |         --------- calling this method introduces a `'static` lifetime requirement
   |
help: relax the implicit `'static` bound on the impl
   |
LL |     impl dyn Foo + '_ {
   |                  ++++
```
```text
error: lifetime may not live long enough
  --> $DIR/static-impl-obligation.rs:173:27
   |
LL |     fn bar<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
LL |         let y: &dyn Foo = x;
   |                           ^ cast requires that `'a` must outlive `'static`
LL |         y.hello();
   |         --------- calling this method introduces a `'static` lifetime requirement
   |
note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements
  --> $DIR/static-impl-obligation.rs:169:20
   |
LL |     impl dyn Foo + 'static where Self: 'static {
   |                    ^^^^^^^             ^^^^^^^
LL |         fn hello(&self) where Self: 'static {}
   |                                     ^^^^^^^
```
  • Loading branch information
estebank committed Feb 19, 2024
1 parent 8a49772 commit 5b76e77
Show file tree
Hide file tree
Showing 18 changed files with 834 additions and 73 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2021"
either = "1.5.0"
itertools = "0.11"
polonius-engine = "0.13.0"
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
Expand Down
306 changes: 262 additions & 44 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2051,6 +2051,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
CRATE_DEF_ID.to_def_id(),
predicate_span,
))
} else if let ConstraintCategory::CallArgument(Some(fn_def)) = constraint.category {
Some(ObligationCauseCode::MethodCallConstraint(fn_def, constraint.span))
} else {
None
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/type_check/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
locations: Locations,
) {
for (predicate, span) in instantiated_predicates {
debug!(?predicate);
debug!(?span, ?predicate);
let category = ConstraintCategory::Predicate(span);
let predicate = self.normalize_with_category(predicate, locations, category);
self.prove_predicate(predicate, locations, category);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Const
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_span::{Span, DUMMY_SP};
use rustc_span::Span;
use rustc_trait_selection::solve::deeply_normalize;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
Expand Down Expand Up @@ -183,7 +183,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {

// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
let origin = infer::RelateParamBound(self.span, t1, None);

TypeOutlives::new(
&mut *self,
Expand Down
16 changes: 10 additions & 6 deletions compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
use rustc_span::{ErrorGuaranteed, DUMMY_SP};
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::solve::deeply_normalize;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
Expand Down Expand Up @@ -269,7 +269,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
debug!("build: input_or_output={:?}", ty);
// We add implied bounds from both the unnormalized and normalized ty.
// See issue #87748
let constraints_unnorm = self.add_implied_bounds(ty);
let constraints_unnorm = self.add_implied_bounds(ty, span);
if let Some(c) = constraints_unnorm {
constraints.push(c)
}
Expand Down Expand Up @@ -299,7 +299,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
// ```
// Both &Self::Bar and &() are WF
if ty != norm_ty {
let constraints_norm = self.add_implied_bounds(norm_ty);
let constraints_norm = self.add_implied_bounds(norm_ty, span);
if let Some(c) = constraints_norm {
constraints.push(c)
}
Expand All @@ -323,7 +323,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {

// We currently add implied bounds from the normalized ty only.
// This is more conservative and matches wfcheck behavior.
let c = self.add_implied_bounds(norm_ty);
let c = self.add_implied_bounds(norm_ty, span);
constraints.extend(c);
}
}
Expand Down Expand Up @@ -361,11 +361,15 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
/// the same time, compute and add any implied bounds that come
/// from this local.
#[instrument(level = "debug", skip(self))]
fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
fn add_implied_bounds(
&mut self,
ty: Ty<'tcx>,
span: Span,
) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
let TypeOpOutput { output: bounds, constraints, .. } = self
.param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self.infcx, DUMMY_SP)
.fully_perform(self.infcx, span)
.map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
.ok()?;
debug!(?bounds, ?constraints);
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};
use rustc_span::Span;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
Expand Down Expand Up @@ -1014,7 +1014,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
) -> Self {
let mut checker = Self {
infcx,
last_span: DUMMY_SP,
last_span: body.span,
body,
user_type_annotations: &body.user_type_annotations,
param_env,
Expand Down Expand Up @@ -2766,7 +2766,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.param_env,
self.known_type_outlives_obligations,
locations,
DUMMY_SP, // irrelevant; will be overridden.
self.body.span, // irrelevant; will be overridden.
ConstraintCategory::Boring, // same as above.
self.borrowck_context.constraints,
)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,7 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>(
#[instrument(level = "debug", skip(tcx))]
fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
let mut result = tcx.explicit_predicates_of(def_id);
debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,);
debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result);
let inferred_outlives = tcx.inferred_outlives_of(def_id);
if !inferred_outlives.is_empty() {
debug!(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// will instantiate fresh inference variables for each canonical
/// variable instead. Therefore, the result of this method must be
/// properly unified
#[instrument(level = "debug", skip(self, cause, param_env))]
#[instrument(level = "debug", skip(self, param_env))]
fn query_response_instantiation_guess<R>(
&self,
cause: &ObligationCause<'tcx>,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,9 @@ pub enum ObligationCauseCode<'tcx> {

/// Obligations emitted during the normalization of a weak type alias.
TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId),

/// During borrowck we've found a method call that could have introduced a lifetime requirement.
MethodCallConstraint(Ty<'tcx>, Span),
}

/// Whether a value can be extracted into a const.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::MethodReceiver
| ObligationCauseCode::ReturnNoExpression
| ObligationCauseCode::UnifyReceiver(..)
| ObligationCauseCode::MethodCallConstraint(..)
| ObligationCauseCode::MiscObligation
| ObligationCauseCode::WellFormed(..)
| ObligationCauseCode::MatchImpl(..)
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
};

let mut constraints = QueryRegionConstraints::default();
let span = infcx.tcx.def_span(body_id);
let Ok(InferOk { value: mut bounds, obligations }) = infcx
.instantiate_nll_query_response_and_region_obligations(
&ObligationCause::dummy(),
&ObligationCause::dummy_with_span(span),
param_env,
&canonical_var_values,
canonical_result,
Expand All @@ -80,8 +81,6 @@ fn implied_outlives_bounds<'a, 'tcx>(
bounds.retain(|bound| !bound.has_placeholders());

if !constraints.is_empty() {
let span = infcx.tcx.def_span(body_id);

debug!(?constraints);
if !constraints.member_constraints.is_empty() {
span_bug!(span, "{:#?}", constraints.member_constraints);
Expand Down
12 changes: 11 additions & 1 deletion tests/ui/inference/issue-80409.no-compat.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ImpliedOutlivesBounds { ty: &'?2 mut StateContext<'?3, usize> } }
--> $DIR/issue-80409.rs:48:30
|
= query stack during panic:
LL | builder.state().on_entry(|_| {});
| ^^^
|
note: delayed at /home/gh-estebank/rust/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs:164:29 - disabled backtrace
--> $DIR/issue-80409.rs:48:30
|
LL | builder.state().on_entry(|_| {});
| ^^^

query stack during panic:
end of query stack
error: aborting due to 1 previous error

195 changes: 195 additions & 0 deletions tests/ui/lifetimes/static-impl-obligation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
mod a {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo { //~ HELP consider relaxing the implicit `'static` requirement
fn hello(&self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod b {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo {
fn hello(&'static self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod c {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo {
fn hello(&'static self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod d {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo {
fn hello(&self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod e {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static { //~ HELP consider replacing this `'static` requirement
fn hello(&self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod f {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static {
fn hello(&'static self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod g {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static {
fn hello(&'static self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod h {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static {
fn hello(&self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod i {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo where Self: 'static {
fn hello(&self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod j {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo where Self: 'static {
fn hello(&'static self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod k {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo where Self: 'static {
fn hello(&'static self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod l {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo where Self: 'static {
fn hello(&self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod m {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static where Self: 'static {
fn hello(&self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod n {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static where Self: 'static {
fn hello(&'static self) {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod o {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static where Self: 'static {
fn hello(&'static self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod p {
trait Foo {}
impl<'a> Foo for &'a u32 {}
impl dyn Foo + 'static where Self: 'static {
fn hello(&self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a u32) {
let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough
y.hello();
}
}
mod q {
struct Foo {}
impl Foo {
fn hello(&'static self) {}
}
fn bar<'a>(x: &'a &'a Foo) {
x.hello(); //~ ERROR borrowed data escapes outside of function
}
}
mod r {
struct Foo {}
impl Foo {
fn hello(&'static self) where Self: 'static {}
}
fn bar<'a>(x: &'a &'a Foo) {
x.hello(); //~ ERROR borrowed data escapes outside of function
}
}
fn main() {}
Loading

0 comments on commit 5b76e77

Please sign in to comment.