about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-17 02:35:06 +0000
committerbors <bors@rust-lang.org>2024-01-17 02:35:06 +0000
commit6bf600bc98879bf1bf2ab0f3b3b37c965f5bdff6 (patch)
tree975c75884dd9f6b6498da1542c325f3250eeeb17
parent6ed31aba1a889b5d5e5362f1cdde316fb0b571d1 (diff)
parent66090ef7ba5f8586879f8029400a1cc851e7c57f (diff)
downloadrust-6bf600bc98879bf1bf2ab0f3b3b37c965f5bdff6.tar.gz
rust-6bf600bc98879bf1bf2ab0f3b3b37c965f5bdff6.zip
Auto merge of #120019 - lcnr:fn-wf, r=BoxyUwU
fix fn/const items implied bounds and wf check (rebase)

A rebase of #104098, see that PR for discussion. This is pretty much entirely the work of `@aliemjay.` I received his permission for this rebase.

---

These are two distinct changes (edit: actually three, see below):
1. Wf-check all fn item args. This is a soundness fix.
Fixes #104005

2. Use implied bounds from impl header in borrowck of associated functions/consts. This strictly accepts more code and helps to mitigate the impact of other breaking changes.
Fixes #98852
Fixes #102611

The first is a breaking change and will likely have a big impact without the the second one. See the first commit for how it breaks libstd.

Landing the second one without the first will allow more incorrect code to pass. For example an exploit of #104005 would be as simple as:
```rust
use core::fmt::Display;

trait ExtendLt<Witness> {
    fn extend(self) -> Box<dyn Display>;
}

impl<T: Display> ExtendLt<&'static T> for T {
    fn extend(self) -> Box<dyn Display> {
        Box::new(self)
    }
}

fn main() {
    let val = (&String::new()).extend();
    println!("{val}");
}
```

The third change is to to check WF of user type annotations before normalizing them (fixes #104764, fixes #104763). It is mutually dependent on the second change above: an attempt to land it separately in #104746 caused several crater regressions that can all be mitigated by using the implied from the impl header. It is also necessary for the soundness of associated consts that use the implied bounds of impl header. See #104763 and how the third commit fixes the soundness issue in `tests/ui/wf/wf-associated-const.rs` that was introduces by the previous commit.

r? types
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs25
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs54
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs17
-rw-r--r--tests/ui/borrowck/fn-item-check-trait-ref.rs15
-rw-r--r--tests/ui/borrowck/fn-item-check-trait-ref.stderr12
-rw-r--r--tests/ui/borrowck/fn-item-check-type-params.rs57
-rw-r--r--tests/ui/borrowck/fn-item-check-type-params.stderr43
-rw-r--r--tests/ui/higher-ranked/trait-bounds/issue-59311.rs1
-rw-r--r--tests/ui/higher-ranked/trait-bounds/issue-59311.stderr11
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs (renamed from tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs)9
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.stderr16
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-2.rs45
-rw-r--r--tests/ui/lifetimes/lifetime-errors/issue_74400.rs2
-rw-r--r--tests/ui/lifetimes/lifetime-errors/issue_74400.stderr32
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.fail.stderr2
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr17
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.rs4
-rw-r--r--tests/ui/wf/wf-associated-const.rs41
-rw-r--r--tests/ui/wf/wf-associated-const.stderr38
-rw-r--r--tests/ui/wf/wf-in-fn-type-implicit.rs37
-rw-r--r--tests/ui/wf/wf-static-method.rs13
-rw-r--r--tests/ui/wf/wf-static-method.stderr47
23 files changed, 425 insertions, 123 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 4a76d877af0..011b5b760c2 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -1,5 +1,6 @@
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
+use rustc_hir::def::DefKind;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -195,7 +196,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     pub(crate) fn create(mut self) -> CreateResult<'tcx> {
-        let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id());
+        let tcx = self.infcx.tcx;
+        let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local();
+        let span = tcx.def_span(defining_ty_def_id);
 
         // Insert the facts we know from the predicates. Why? Why not.
         let param_env = self.param_env;
@@ -275,6 +278,26 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
             normalized_inputs_and_output.push(norm_ty);
         }
 
+        // Add implied bounds from impl header.
+        if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) {
+            for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
+                let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = self
+                    .param_env
+                    .and(type_op::normalize::Normalize::new(ty))
+                    .fully_perform(self.infcx, span)
+                else {
+                    tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}"));
+                    continue;
+                };
+                constraints.extend(c);
+
+                // 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);
+                constraints.extend(c);
+            }
+        }
+
         for c in constraints {
             self.push_region_constraints(c, span);
         }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 33190e19104..aa951a6ce55 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -407,6 +407,16 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                     instantiated_predicates,
                     locations,
                 );
+
+                assert!(!matches!(
+                    tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)),
+                    Some(DefKind::Impl { of_trait: true })
+                ));
+                self.cx.prove_predicates(
+                    args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())),
+                    locations,
+                    ConstraintCategory::Boring,
+                );
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index 152ceeee869..0aaeacb6a23 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -63,13 +63,16 @@ fn relate_mir_and_user_ty<'tcx>(
     user_ty: Ty<'tcx>,
 ) -> Result<(), NoSolution> {
     let cause = ObligationCause::dummy_with_span(span);
+    ocx.register_obligation(Obligation::new(
+        ocx.infcx.tcx,
+        cause.clone(),
+        param_env,
+        ty::ClauseKind::WellFormed(user_ty.into()),
+    ));
+
     let user_ty = ocx.normalize(&cause, param_env, user_ty);
     ocx.eq(&cause, param_env, mir_ty, user_ty)?;
 
-    // FIXME(#104764): We should check well-formedness before normalization.
-    let predicate =
-        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into())));
-    ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
     Ok(())
 }
 
@@ -113,31 +116,38 @@ fn relate_mir_and_user_args<'tcx>(
         ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
     }
 
+    // Now prove the well-formedness of `def_id` with `substs`.
+    // Note for some items, proving the WF of `ty` is not sufficient because the
+    // well-formedness of an item may depend on the WF of gneneric args not present in the
+    // item's type. Currently this is true for associated consts, e.g.:
+    // ```rust
+    // impl<T> MyTy<T> {
+    //     const CONST: () = { /* arbitrary code that depends on T being WF */ };
+    // }
+    // ```
+    for arg in args {
+        ocx.register_obligation(Obligation::new(
+            tcx,
+            cause.clone(),
+            param_env,
+            ty::ClauseKind::WellFormed(arg),
+        ));
+    }
+
     if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+        ocx.register_obligation(Obligation::new(
+            tcx,
+            cause.clone(),
+            param_env,
+            ty::ClauseKind::WellFormed(self_ty.into()),
+        ));
+
         let self_ty = ocx.normalize(&cause, param_env, self_ty);
         let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args);
         let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
 
         ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
-        let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
-            impl_self_ty.into(),
-        )));
-        ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
     }
 
-    // In addition to proving the predicates, we have to
-    // prove that `ty` is well-formed -- this is because
-    // the WF of `ty` is predicated on the args being
-    // well-formed, and we haven't proven *that*. We don't
-    // want to prove the WF of types from  `args` directly because they
-    // haven't been normalized.
-    //
-    // FIXME(nmatsakis): Well, perhaps we should normalize
-    // them?  This would only be relevant if some input
-    // type were ill-formed but did not appear in `ty`,
-    // which...could happen with normalization...
-    let predicate =
-        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into())));
-    ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
     Ok(())
 }
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
index e21ede47f6d..14f14ae6e2e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -30,6 +30,23 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
             }
         }
 
+        if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) =
+            key.value.predicate.kind().skip_binder()
+        {
+            match arg.as_type()?.kind() {
+                ty::Param(_)
+                | ty::Bool
+                | ty::Char
+                | ty::Int(_)
+                | ty::Float(_)
+                | ty::Str
+                | ty::Uint(_) => {
+                    return Some(());
+                }
+                _ => {}
+            }
+        }
+
         None
     }
 
diff --git a/tests/ui/borrowck/fn-item-check-trait-ref.rs b/tests/ui/borrowck/fn-item-check-trait-ref.rs
new file mode 100644
index 00000000000..bdbb52e974f
--- /dev/null
+++ b/tests/ui/borrowck/fn-item-check-trait-ref.rs
@@ -0,0 +1,15 @@
+// The method `assert_static` should be callable only for static values,
+// because the impl has an implied bound `where T: 'static`.
+
+// check-fail
+
+trait AnyStatic<Witness>: Sized {
+    fn assert_static(self) {}
+}
+
+impl<T> AnyStatic<&'static T> for T {}
+
+fn main() {
+    (&String::new()).assert_static();
+    //~^ ERROR temporary value dropped while borrowed
+}
diff --git a/tests/ui/borrowck/fn-item-check-trait-ref.stderr b/tests/ui/borrowck/fn-item-check-trait-ref.stderr
new file mode 100644
index 00000000000..92fd67c316c
--- /dev/null
+++ b/tests/ui/borrowck/fn-item-check-trait-ref.stderr
@@ -0,0 +1,12 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/fn-item-check-trait-ref.rs:13:7
+   |
+LL |     (&String::new()).assert_static();
+   |     --^^^^^^^^^^^^^------------------ temporary value is freed at the end of this statement
+   |     | |
+   |     | creates a temporary value which is freed while still in use
+   |     argument requires that borrow lasts for `'static`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/borrowck/fn-item-check-type-params.rs b/tests/ui/borrowck/fn-item-check-type-params.rs
new file mode 100644
index 00000000000..805c0d00de5
--- /dev/null
+++ b/tests/ui/borrowck/fn-item-check-type-params.rs
@@ -0,0 +1,57 @@
+// Regression test for #104005.
+//
+// Previously, different borrowck implementations used to disagree here.
+// The status of each is documented on `fn test_*`.
+
+// check-fail
+
+use std::fmt::Display;
+
+trait Displayable {
+    fn display(self) -> Box<dyn Display>;
+}
+
+impl<T: Display> Displayable for (T, Option<&'static T>) {
+    fn display(self) -> Box<dyn Display> {
+        Box::new(self.0)
+    }
+}
+
+fn extend_lt<T, U>(val: T) -> Box<dyn Display>
+where
+    (T, Option<U>): Displayable,
+{
+    Displayable::display((val, None))
+}
+
+// AST: fail
+// HIR: pass
+// MIR: pass
+pub fn test_call<'a>(val: &'a str) {
+    extend_lt(val);
+    //~^ ERROR borrowed data escapes outside of function
+}
+
+// AST: fail
+// HIR: fail
+// MIR: pass
+pub fn test_coercion<'a>() {
+    let _: fn(&'a str) -> _ = extend_lt;
+    //~^ ERROR lifetime may not live long enough
+}
+
+// AST: fail
+// HIR: fail
+// MIR: fail
+pub fn test_arg() {
+    fn want<I, O>(_: I, _: impl Fn(I) -> O) {}
+    want(&String::new(), extend_lt);
+    //~^ ERROR temporary value dropped while borrowed
+}
+
+// An exploit of the unsoundness.
+fn main() {
+    let val = extend_lt(&String::from("blah blah blah"));
+    //~^ ERROR temporary value dropped while borrowed
+    println!("{}", val);
+}
diff --git a/tests/ui/borrowck/fn-item-check-type-params.stderr b/tests/ui/borrowck/fn-item-check-type-params.stderr
new file mode 100644
index 00000000000..3a29edc55c5
--- /dev/null
+++ b/tests/ui/borrowck/fn-item-check-type-params.stderr
@@ -0,0 +1,43 @@
+error[E0521]: borrowed data escapes outside of function
+  --> $DIR/fn-item-check-type-params.rs:31:5
+   |
+LL | pub fn test_call<'a>(val: &'a str) {
+   |                  --  --- `val` is a reference that is only valid in the function body
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     extend_lt(val);
+   |     ^^^^^^^^^^^^^^
+   |     |
+   |     `val` escapes the function body here
+   |     argument requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/fn-item-check-type-params.rs:39:12
+   |
+LL | pub fn test_coercion<'a>() {
+   |                      -- lifetime `'a` defined here
+LL |     let _: fn(&'a str) -> _ = extend_lt;
+   |            ^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/fn-item-check-type-params.rs:48:11
+   |
+LL |     want(&String::new(), extend_lt);
+   |     ------^^^^^^^^^^^^^------------- temporary value is freed at the end of this statement
+   |     |     |
+   |     |     creates a temporary value which is freed while still in use
+   |     argument requires that borrow lasts for `'static`
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/fn-item-check-type-params.rs:54:26
+   |
+LL |     let val = extend_lt(&String::from("blah blah blah"));
+   |               -----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement
+   |               |          |
+   |               |          creates a temporary value which is freed while still in use
+   |               argument requires that borrow lasts for `'static`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0521, E0716.
+For more information about an error, try `rustc --explain E0521`.
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs
index 3ad548450e5..387c78a802a 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs
+++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs
@@ -17,6 +17,7 @@ where
     v.t(|| {});
     //~^ ERROR: higher-ranked lifetime error
     //~| ERROR: higher-ranked lifetime error
+    //~| ERROR: higher-ranked lifetime error
 }
 
 fn main() {}
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
index e50aad876d8..3053a299802 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
@@ -7,6 +7,15 @@ LL |     v.t(|| {});
    = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed`
 
 error: higher-ranked lifetime error
+  --> $DIR/issue-59311.rs:17:5
+   |
+LL |     v.t(|| {});
+   |     ^^^^^^^^^^
+   |
+   = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: higher-ranked lifetime error
   --> $DIR/issue-59311.rs:17:9
    |
 LL |     v.t(|| {});
@@ -14,5 +23,5 @@ LL |     v.t(|| {});
    |
    = note: could not prove `for<'a> &'a V: 'b`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs
index 9c26cd59d10..b532a110a1c 100644
--- a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs
+++ b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs
@@ -1,9 +1,7 @@
-// check-pass
-// known-bug: #84591
+// issue: #84591
 
-// Should fail. Subtrait can incorrectly extend supertrait lifetimes even when
-// supertrait has weaker implied bounds than subtrait. Strongly related to
-// issue #25860.
+// Subtrait was able to incorrectly extend supertrait lifetimes even when
+// supertrait had weaker implied bounds than subtrait.
 
 trait Subtrait<T>: Supertrait {}
 trait Supertrait {
@@ -34,6 +32,7 @@ fn main() {
     {
         let x = "Hello World".to_string();
         subs_to_soup((x.as_str(), &mut d));
+        //~^ does not live long enough
     }
     println!("{}", d);
 }
diff --git a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.stderr b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.stderr
new file mode 100644
index 00000000000..f44defccf58
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.stderr
@@ -0,0 +1,16 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/implied-bounds-on-trait-hierarchy-1.rs:34:23
+   |
+LL |         let x = "Hello World".to_string();
+   |             - binding `x` declared here
+LL |         subs_to_soup((x.as_str(), &mut d));
+   |                       ^ borrowed value does not live long enough
+LL |
+LL |     }
+   |     - `x` dropped here while still borrowed
+LL |     println!("{}", d);
+   |                    - borrow later used here
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-2.rs b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-2.rs
new file mode 100644
index 00000000000..511a9ad9a2a
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-2.rs
@@ -0,0 +1,45 @@
+// check-pass
+// known-bug: #84591
+
+trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {}
+trait Supertrait<'a, 'b> {
+    fn convert<T: ?Sized>(x: &'a T) -> &'b T;
+}
+
+fn need_hrtb_subtrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T
+where
+    S: for<'a, 'b> Subtrait<'a, 'b, &'b &'a ()>,
+{
+    need_hrtb_supertrait::<S, T>(x)
+    // This call works and drops the implied bound `'a: 'b`
+    // of the where-bound. This means the where-bound can
+    // now be used to transmute any two lifetimes.
+}
+
+fn need_hrtb_supertrait<'a_, 'b_, S, T: ?Sized>(x: &'a_ T) -> &'b_ T
+where
+    S: for<'a, 'b> Supertrait<'a, 'b>,
+{
+    S::convert(x)
+}
+
+struct MyStruct;
+impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct {
+    fn convert<T: ?Sized>(x: &'a T) -> &'b T {
+        x
+    }
+}
+impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {}
+
+fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
+    need_hrtb_subtrait::<MyStruct, T>(x)
+}
+
+fn main() {
+    let d;
+    {
+        let x = "Hello World".to_string();
+        d = extend_lifetime(&x);
+    }
+    println!("{}", d);
+}
diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
index ddb8bacce8f..f17e0a678c9 100644
--- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
+++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs
@@ -11,6 +11,8 @@ fn f<T, S>(data: &[T], key: impl Fn(&T) -> S) {
 fn g<T>(data: &[T]) {
     f(data, identity)
     //~^ ERROR the parameter type
+    //~| ERROR the parameter type
+    //~| ERROR the parameter type
     //~| ERROR mismatched types
     //~| ERROR implementation of `FnOnce` is not general
 }
diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr
index 677f918fe93..beb838d2ff8 100644
--- a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr
+++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr
@@ -12,6 +12,36 @@ help: consider adding an explicit lifetime bound
 LL | fn g<T: 'static>(data: &[T]) {
    |       +++++++++
 
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/issue_74400.rs:12:5
+   |
+LL |     f(data, identity)
+   |     ^^^^^^^^^^^^^^^^^
+   |     |
+   |     the parameter type `T` must be valid for the static lifetime...
+   |     ...so that the type `T` will meet its required lifetime bounds
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider adding an explicit lifetime bound
+   |
+LL | fn g<T: 'static>(data: &[T]) {
+   |       +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/issue_74400.rs:12:5
+   |
+LL |     f(data, identity)
+   |     ^^^^^^^^^^^^^^^^^
+   |     |
+   |     the parameter type `T` must be valid for the static lifetime...
+   |     ...so that the type `T` will meet its required lifetime bounds
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider adding an explicit lifetime bound
+   |
+LL | fn g<T: 'static>(data: &[T]) {
+   |       +++++++++
+
 error[E0308]: mismatched types
   --> $DIR/issue_74400.rs:12:5
    |
@@ -35,7 +65,7 @@ LL |     f(data, identity)
    = note: `fn(&'2 T) -> &'2 T {identity::<&'2 T>}` must implement `FnOnce<(&'1 T,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&'2 T,)>`, for some specific lifetime `'2`
 
-error: aborting due to 3 previous errors
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0308, E0310.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
index dcc4b8021ea..17c1f8897bf 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
+++ b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
@@ -1,5 +1,5 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/wf-nested.rs:55:27
+  --> $DIR/wf-nested.rs:57:27
    |
 LL |     type InnerOpaque<T> = impl Sized;
    |                           ^^^^^^^^^^
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
index 121664bd956..f5d3a218542 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
+++ b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
@@ -12,6 +12,21 @@ help: consider adding an explicit lifetime bound
 LL |     fn test<T: 'static>() {
    |              +++++++++
 
-error: aborting due to 1 previous error
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:46:17
+   |
+LL |         let _ = outer.get();
+   |                 ^^^^^^^^^^^
+   |                 |
+   |                 the parameter type `T` must be valid for the static lifetime...
+   |                 ...so that the type `T` will meet its required lifetime bounds
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider adding an explicit lifetime bound
+   |
+LL |     fn test<T: 'static>() {
+   |              +++++++++
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0310`.
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.rs b/tests/ui/type-alias-impl-trait/wf-nested.rs
index de388329489..2f90c4e00e3 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.rs
+++ b/tests/ui/type-alias-impl-trait/wf-nested.rs
@@ -43,7 +43,9 @@ mod pass_sound {
 
     fn test<T>() {
         let outer = define::<T>();
-        let _ = outer.get(); //[pass_sound]~ ERROR `T` may not live long enough
+        let _ = outer.get();
+        //[pass_sound]~^ ERROR `T` may not live long enough
+        //[pass_sound]~| ERROR `T` may not live long enough
     }
 }
 
diff --git a/tests/ui/wf/wf-associated-const.rs b/tests/ui/wf/wf-associated-const.rs
new file mode 100644
index 00000000000..629d807cb7f
--- /dev/null
+++ b/tests/ui/wf/wf-associated-const.rs
@@ -0,0 +1,41 @@
+// check that associated consts can assume the impl header is well-formed.
+
+trait Foo<'a, 'b, T>: Sized {
+    const EVIL: fn(u: &'b u32) -> &'a u32;
+}
+
+struct Evil<'a, 'b: 'a>(Option<&'a &'b ()>);
+
+impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
+    const EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
+}
+
+struct IndirectEvil<'a, 'b: 'a>(Option<&'a &'b ()>);
+
+impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
+    const EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
+}
+
+impl<'a, 'b> Evil<'a, 'b> {
+    const INHERENT_EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
+}
+
+// while static methods can *assume* this, we should still
+// *check* that it holds at the use site.
+
+fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+    <()>::EVIL(b)
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+    <IndirectEvil>::EVIL(b)
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+    <Evil>::INHERENT_EVIL(b)
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn main() {}
diff --git a/tests/ui/wf/wf-associated-const.stderr b/tests/ui/wf/wf-associated-const.stderr
new file mode 100644
index 00000000000..b0e1a118fab
--- /dev/null
+++ b/tests/ui/wf/wf-associated-const.stderr
@@ -0,0 +1,38 @@
+error: lifetime may not live long enough
+  --> $DIR/wf-associated-const.rs:27:5
+   |
+LL | fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+   |         --  -- lifetime `'b` defined here
+   |         |
+   |         lifetime `'a` defined here
+LL |     <()>::EVIL(b)
+   |     ^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+
+error: lifetime may not live long enough
+  --> $DIR/wf-associated-const.rs:32:5
+   |
+LL | fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     <IndirectEvil>::EVIL(b)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+
+error: lifetime may not live long enough
+  --> $DIR/wf-associated-const.rs:37:5
+   |
+LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     <Evil>::INHERENT_EVIL(b)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/wf/wf-in-fn-type-implicit.rs b/tests/ui/wf/wf-in-fn-type-implicit.rs
deleted file mode 100644
index c5ff92c8875..00000000000
--- a/tests/ui/wf/wf-in-fn-type-implicit.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// check-pass
-// known-bug: #104005
-
-// Should fail. Function type parameters with implicit type annotations are not
-// checked for well-formedness, which allows incorrect borrowing.
-
-// In contrast, user annotations are always checked for well-formedness, and the
-// commented code below is correctly rejected by the borrow checker.
-
-use std::fmt::Display;
-
-trait Displayable {
-    fn display(self) -> Box<dyn Display>;
-}
-
-impl<T: Display> Displayable for (T, Option<&'static T>) {
-    fn display(self) -> Box<dyn Display> {
-        Box::new(self.0)
-    }
-}
-
-fn extend_lt<T, U>(val: T) -> Box<dyn Display>
-where
-    (T, Option<U>): Displayable,
-{
-    Displayable::display((val, None))
-}
-
-fn main() {
-    // *incorrectly* compiles
-    let val = extend_lt(&String::from("blah blah blah"));
-    println!("{}", val);
-
-    // *correctly* fails to compile
-    // let val = extend_lt::<_, &_>(&String::from("blah blah blah"));
-    // println!("{}", val);
-}
diff --git a/tests/ui/wf/wf-static-method.rs b/tests/ui/wf/wf-static-method.rs
index 7ff195230bf..66546ac489d 100644
--- a/tests/ui/wf/wf-static-method.rs
+++ b/tests/ui/wf/wf-static-method.rs
@@ -1,8 +1,4 @@
-// check that static methods don't get to assume their trait-ref
-// is well-formed.
-// FIXME(#27579): this is just a bug. However, our checking with
-// static inherent methods isn't quite working - need to
-// fix that before removing the check.
+// check that static methods can assume their trait-ref is well-formed.
 
 trait Foo<'a, 'b, T>: Sized {
     fn make_me() -> Self { loop {} }
@@ -15,7 +11,6 @@ impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
     fn make_me() -> Self { }
     fn static_evil(u: &'b u32) -> &'a u32 {
         u
-        //~^ ERROR lifetime may not live long enough
     }
 }
 
@@ -25,7 +20,6 @@ impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
     fn make_me() -> Self { IndirectEvil(None) }
     fn static_evil(u: &'b u32) -> &'a u32 {
         let me = Self::make_me();
-        //~^ ERROR lifetime may not live long enough
         loop {} // (`me` could be used for the lifetime transmute).
     }
 }
@@ -33,12 +27,11 @@ impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
 impl<'a, 'b> Evil<'a, 'b> {
     fn inherent_evil(u: &'b u32) -> &'a u32 {
         u
-        //~^ ERROR lifetime may not live long enough
     }
 }
 
-// while static methods don't get to *assume* this, we still
-// *check* that they hold.
+// while static methods can *assume* this, we should still
+// *check* that it holds at the use site.
 
 fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
     <()>::static_evil(b)
diff --git a/tests/ui/wf/wf-static-method.stderr b/tests/ui/wf/wf-static-method.stderr
index 161609a5f86..6c49098aad3 100644
--- a/tests/ui/wf/wf-static-method.stderr
+++ b/tests/ui/wf/wf-static-method.stderr
@@ -1,44 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:17:9
-   |
-LL | impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
-   |      --  -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-...
-LL |         u
-   |         ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
-   |
-   = help: consider adding the following bound: `'b: 'a`
-
-error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:27:18
-   |
-LL | impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
-   |      --  -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-...
-LL |         let me = Self::make_me();
-   |                  ^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
-   |
-   = help: consider adding the following bound: `'b: 'a`
-
-error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:35:9
-   |
-LL | impl<'a, 'b> Evil<'a, 'b> {
-   |      --  -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-LL |     fn inherent_evil(u: &'b u32) -> &'a u32 {
-LL |         u
-   |         ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
-   |
-   = help: consider adding the following bound: `'b: 'a`
-
-error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:44:5
+  --> $DIR/wf-static-method.rs:37:5
    |
 LL | fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
    |         --  -- lifetime `'b` defined here
@@ -50,7 +11,7 @@ LL |     <()>::static_evil(b)
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:49:5
+  --> $DIR/wf-static-method.rs:42:5
    |
 LL | fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
    |                  --  -- lifetime `'b` defined here
@@ -62,7 +23,7 @@ LL |     <IndirectEvil>::static_evil(b)
    = help: consider adding the following bound: `'b: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/wf-static-method.rs:54:5
+  --> $DIR/wf-static-method.rs:47:5
    |
 LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
    |                  --  -- lifetime `'b` defined here
@@ -73,5 +34,5 @@ LL |     <Evil>::inherent_evil(b)
    |
    = help: consider adding the following bound: `'b: 'a`
 
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors