diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 2bf73e5da7e34..7f09d660bdb86 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -279,8 +279,38 @@ impl<'tcx> InferCtxt<'tcx> { b, a, category, ); } - // FIXME(-Zassumptions-on-binders): actually implement OR as an OR - And(nested) | Or(nested) => constraints.extend(nested), + And(nested) => { + debug_assert!(!nested.iter().any(|c| c.is_ambig())); + constraints.extend(nested); + } + // FIXME(-Zassumptions-on-binders): actually implement OR as an OR. + // Mixed `Or` may contain `Ambiguity` beside remaining candidates + // (`evaluate_solver_constraint` keeps that sibling so unknown ∨ + // later-false does not become false). Don't emit the + // unknown-implied-bounds error while a concrete candidate remains. + // `Or([])` is false: we drop it the same way `extend` does on an + // empty slice. A root-false constraint has nothing to register; + // unsatisfied outlives are reported later by borrowck/regionck. + Or(nested) => { + let mut ambiguity = None; + let concrete: Vec<_> = nested + .into_iter() + .filter_map(|c| match c { + Ambiguity(span) => { + ambiguity.get_or_insert(span); + None + } + c => Some(c), + }) + .collect(); + if concrete.is_empty() { + if let Some(span) = ambiguity { + constraints.push(Ambiguity(span)); + } + } else { + constraints.extend(concrete); + } + } AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => unreachable!(), } } diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 128735965ba73..f71123b60e66e 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -14,6 +14,7 @@ use std::iter; use canonicalizer::Canonicalizer; use rustc_index::IndexVec; use rustc_type_ir::inherent::*; +use rustc_type_ir::region_constraint::RegionConstraint as SolverRegionConstraint; use rustc_type_ir::relate::{ self, Relate, RelateResult, TypeRelation, VarianceDiagInfo, relate_args_invariantly, }; @@ -415,7 +416,20 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { - self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); + if self.cx().assumptions_on_binders() { + if a != b { + self.infcx.register_solver_region_constraint( + SolverRegionConstraint::RegionOutlives(a, b, ()), + self.span, + ); + self.infcx.register_solver_region_constraint( + SolverRegionConstraint::RegionOutlives(b, a, ()), + self.span, + ); + } + } else { + self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span); + } Ok(a) } diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 2592c0579c741..09d6e5110ff7f 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -45,7 +45,7 @@ impl Default for TransitiveRelationBuilder { } } -use crate::data_structures::IndexMap; +use crate::data_structures::{HashMap, HashSet, IndexMap}; use crate::fold::TypeSuperFoldable; use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; @@ -485,6 +485,12 @@ pub fn eagerly_handle_placeholders_in_universe, I: Interner>( + infcx: &Infcx, + constraint: RegionConstraint, + u: UniverseIndex, +) -> RegionConstraint { + use RegionConstraint::*; + + match constraint { + Ambiguity(_) + | RegionOutlives(..) + | PlaceholderTyOutlives(..) + | AliasTyOutlivesViaEnv(..) => constraint, + Or(constraints) => Or(constraints + .into_iter() + .map(|constraint| normalize_equated_region_vars(infcx, constraint, u)) + .collect()), + And(constraints) => { + let constraint = And(constraints + .into_iter() + .map(|constraint| normalize_equated_region_vars(infcx, constraint, u)) + .collect()); + + let mut region_outlives = vec![]; + collect_conjunctive_region_outlives(&constraint, &mut region_outlives); + let replacements = compute_equated_region_var_replacements(infcx, ®ion_outlives, u); + + if replacements.is_empty() { + constraint + } else { + constraint.fold_with(&mut EquatedRegionVarReplacer { cx: infcx.cx(), replacements }) + } + } + } +} + +fn compute_equated_region_var_replacements, I: Interner>( + infcx: &Infcx, + region_outlives: &[(Region, Region)], + u: UniverseIndex, +) -> HashMap, Region> { + compute_equated_region_var_replacements_from( + region_outlives, + |r| is_current_universe_region_var(infcx, r, u), + is_region_var::, + ) +} + +fn compute_equated_region_var_replacements_from( + region_outlives: &[(R, R)], + mut is_current_universe_region_var: impl FnMut(R) -> bool, + mut is_region_var: impl FnMut(R) -> bool, +) -> HashMap +where + R: Copy + Eq + std::hash::Hash, +{ + let edges: HashSet<(R, R)> = region_outlives.iter().copied().collect(); + + let mut equated_regions_builder = TransitiveRelationBuilder::default(); + let mut has_equated_regions = false; + for (r1, r2) in region_outlives.iter().copied() { + // Paired outlives constraints represent region equality. Build a transitive relation so + // current-universe variables equated through other variables still find a non-var partner. + if edges.contains(&(r2, r1)) { + equated_regions_builder.add(r1, r2); + equated_regions_builder.add(r2, r1); + has_equated_regions = true; + } + } + + if !has_equated_regions { + return HashMap::default(); + } + + let equated_regions = equated_regions_builder.freeze(); + let mut seen = HashSet::default(); + let mut replacements = HashMap::default(); + for (r1, r2) in region_outlives.iter().copied() { + for candidate in [r1, r2] { + if !seen.insert(candidate) || !is_current_universe_region_var(candidate) { + continue; + } + + // `reachable_from` already includes `candidate` when both equality edges exist. + // Candidates are always revars, so the partner has to come from that closure. + // If a var has several non-var partners, `find` just picks one; the remaining + // folded constraints still relate those partners, so first-match only affects + // representation. + if let Some(partner) = + equated_regions.reachable_from(candidate).into_iter().find(|r| !is_region_var(*r)) + { + replacements.insert(candidate, partner); + } + } + } + replacements +} + +fn collect_conjunctive_region_outlives( + constraint: &RegionConstraint, + out: &mut Vec<(Region, Region)>, +) { + use RegionConstraint::*; + + match constraint { + RegionOutlives(r1, r2, _) => out.push((*r1, *r2)), + And(constraints) => { + for constraint in constraints.iter() { + collect_conjunctive_region_outlives(constraint, out); + } + } + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) | Or(..) => {} + } +} + +fn is_current_universe_region_var, I: Interner>( + infcx: &Infcx, + region: Region, + u: UniverseIndex, +) -> bool { + is_region_var::(region) && max_universe(infcx, region) == u +} + +fn is_region_var(region: Region) -> bool { + matches!(region.kind(), RegionKind::ReVar(_)) +} + +struct EquatedRegionVarReplacer { + cx: I, + replacements: HashMap, Region>, +} + +impl TypeFolder for EquatedRegionVarReplacer { + fn cx(&self) -> I { + self.cx + } + + fn fold_region(&mut self, r: Region) -> Region { + self.replacements.get(&r).copied().unwrap_or(r) + } +} + /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: /// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two @@ -579,7 +726,65 @@ fn compute_new_region_constraints, I: Interne new_constraints } -/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous +/// Already-evaluated OR member, used by [`combine_or`]. +#[derive(Clone, Debug, PartialEq, Eq)] +enum EvaluatedOrMember { + True, + False, + Ambiguity, + Other(T), +} + +/// Kleene OR of already-evaluated members. +#[derive(Clone, Debug, PartialEq, Eq)] +enum CombinedOr { + True, + False, + Ambiguity, + /// Still-open candidates. `plus_ambiguity` means an unknown sibling must + /// stay beside them: unknown ∨ remaining must not drop the unknown. + Or { + remaining: Vec, + plus_ambiguity: bool, + }, +} + +/// Combine already-evaluated OR members. +/// +/// `true ∨ x = true`. `false` members are dropped. `unknown ∨ remaining` keeps +/// both: collapsing to `Ambiguity` drops candidates a later universe may still +/// satisfy, and dropping `Ambiguity` makes a later-false remaining collapse +/// unknown ∨ false to false (spurious `NoSolution`). +fn combine_or(members: impl IntoIterator>) -> CombinedOr { + let mut remaining = Vec::new(); + let mut plus_ambiguity = false; + for member in members { + match member { + EvaluatedOrMember::True => return CombinedOr::True, + EvaluatedOrMember::False => {} + EvaluatedOrMember::Ambiguity => plus_ambiguity = true, + EvaluatedOrMember::Other(c) => remaining.push(c), + } + } + + if remaining.is_empty() { + if plus_ambiguity { CombinedOr::Ambiguity } else { CombinedOr::False } + } else { + CombinedOr::Or { remaining, plus_ambiguity } + } +} + +/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are +/// true/false/ambiguous. +/// +/// `Or(Ambiguity, remaining)` keeps both. Collapsing to `Ambiguity` drops +/// candidates a later universe (or the root) may still satisfy. Dropping the +/// `Ambiguity` sibling is also wrong: if `remaining` later becomes false, +/// unknown ∨ false would become false. +/// +/// `And` is the other way: one ambiguous conjunct makes the whole conjunction +/// unknown, so we still collapse. That is deliberate, not an oversight relative +/// to `Or`. #[instrument(level = "debug", ret)] pub fn evaluate_solver_constraint( constraint: &RegionConstraint, @@ -612,25 +817,32 @@ pub fn evaluate_solver_constraint( ) } Or(or) => { - let mut or_constraints = Vec::new(); let mut ambiguity = None; - for c in or.iter() { + let members = or.iter().map(|c| { let evaluated_constraint = evaluate_solver_constraint(c); - if evaluated_constraint.is_false() { - // do nothing - } else if evaluated_constraint.is_true() { - return RegionConstraint::new_true(); + if evaluated_constraint.is_true() { + EvaluatedOrMember::True + } else if evaluated_constraint.is_false() { + EvaluatedOrMember::False } else if let Ambiguity(span) = evaluated_constraint { ambiguity.get_or_insert(span); + EvaluatedOrMember::Ambiguity } else { - or_constraints.push(evaluated_constraint); + EvaluatedOrMember::Other(evaluated_constraint) + } + }); + + match combine_or(members) { + CombinedOr::True => RegionConstraint::new_true(), + CombinedOr::False => RegionConstraint::new_false(), + CombinedOr::Ambiguity => RegionConstraint::Ambiguity(ambiguity.unwrap()), + CombinedOr::Or { mut remaining, plus_ambiguity } => { + if plus_ambiguity { + remaining.push(RegionConstraint::Ambiguity(ambiguity.unwrap())); + } + RegionConstraint::Or(remaining.into_boxed_slice()) } } - - ambiguity.map_or_else( - || RegionConstraint::Or(or_constraints.into_boxed_slice()), - RegionConstraint::Ambiguity, - ) } } } @@ -681,6 +893,12 @@ fn pull_region_outlives_constraints_out_of_universe< constraint } RegionOutlives(region_1, region_2, ()) => { + if region_1 == region_2 { + // `'r: 'r` is always true, including for current-universe regions. + // Relating a region to itself, component destructure, and normalize + // rewriting `'?x: '!a` + `'!a: '?x` into `'!a: '!a` can all produce this. + return RegionConstraint::new_true(); + } let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); @@ -1175,3 +1393,6 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation Ok(a) } } + +#[cfg(all(test, feature = "nightly"))] +mod tests; diff --git a/compiler/rustc_type_ir/src/region_constraint/tests.rs b/compiler/rustc_type_ir/src/region_constraint/tests.rs new file mode 100644 index 0000000000000..463de9cad3cb8 --- /dev/null +++ b/compiler/rustc_type_ir/src/region_constraint/tests.rs @@ -0,0 +1,44 @@ +use super::{ + CombinedOr, EvaluatedOrMember, combine_or, compute_equated_region_var_replacements_from, +}; + +#[test] +fn equated_region_var_replacements_follow_transitive_region_var_chains() { + const REVAR_1: u8 = 1; + const REVAR_2: u8 = 2; + const PLACEHOLDER: u8 = 3; + // Equated with REVAR_1, but not a current-universe candidate and not a valid partner. + const OTHER_REVAR: u8 = 4; + + let region_outlives = [ + (REVAR_1, REVAR_2), + (REVAR_2, REVAR_1), + (REVAR_2, PLACEHOLDER), + (PLACEHOLDER, REVAR_2), + (REVAR_1, OTHER_REVAR), + (OTHER_REVAR, REVAR_1), + ]; + + let replacements = compute_equated_region_var_replacements_from( + ®ion_outlives, + |r| matches!(r, REVAR_1 | REVAR_2), + |r| matches!(r, REVAR_1 | REVAR_2 | OTHER_REVAR), + ); + + assert_eq!(replacements.len(), 2); + assert_eq!(replacements.get(&REVAR_1), Some(&PLACEHOLDER)); + assert_eq!(replacements.get(&REVAR_2), Some(&PLACEHOLDER)); +} + +/// Mixed `Or(Ambiguity, remaining)` must keep the unknown sibling. A later +/// evaluation where `remaining` becomes false is unknown ∨ false = unknown, +/// not false. +#[test] +fn mixed_or_later_false_candidate_stays_ambiguous() { + let first = combine_or([EvaluatedOrMember::Ambiguity, EvaluatedOrMember::Other("cand")]); + assert_eq!(first, CombinedOr::Or { remaining: vec!["cand"], plus_ambiguity: true }); + + // Second evaluation: the deferred candidate rewrote to false. + let second = combine_or([EvaluatedOrMember::<&str>::Ambiguity, EvaluatedOrMember::False]); + assert_eq!(second, CombinedOr::Ambiguity); +} diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 1e8ff77e4d395..6b752ea0a0e65 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -2,6 +2,7 @@ use tracing::{debug, instrument}; use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}; use crate::data_structures::DelayedSet; +use crate::region_constraint::RegionConstraint; use crate::relate::combine::combine_ty_args; pub use crate::relate::*; use crate::solve::{Goal, VisibleForLeakCheck}; @@ -238,14 +239,43 @@ where #[instrument(skip(self), level = "trace")] fn regions(&mut self, a: Region, b: Region) -> RelateResult> { - match self.ambient_variance { - // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) - ty::Covariant => self.infcx.sub_regions(b, a, VisibleForLeakCheck::Yes, self.span), - // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b) - ty::Contravariant => self.infcx.sub_regions(a, b, VisibleForLeakCheck::Yes, self.span), - ty::Invariant => self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span), - ty::Bivariant => { - unreachable!("Expected bivariance to be handled in relate_with_variance") + if self.cx().assumptions_on_binders() { + match self.ambient_variance { + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") + } + _ if a == b => return Ok(a), + ty::Covariant => self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(a, b, ()), + self.span, + ), + ty::Contravariant => self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(b, a, ()), + self.span, + ), + ty::Invariant => { + self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(a, b, ()), + self.span, + ); + self.infcx.register_solver_region_constraint( + RegionConstraint::RegionOutlives(b, a, ()), + self.span, + ); + } + } + } else { + match self.ambient_variance { + ty::Covariant => self.infcx.sub_regions(b, a, VisibleForLeakCheck::Yes, self.span), + ty::Contravariant => { + self.infcx.sub_regions(a, b, VisibleForLeakCheck::Yes, self.span) + } + ty::Invariant => { + self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span) + } + ty::Bivariant => { + unreachable!("Expected bivariance to be handled in relate_with_variance") + } } } diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index 0c2ed6585cf45..46233daf8dd80 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -3,6 +3,11 @@ // test that a `::Assoc: '!a_u1` constraint is considered to be satisfied // if there's a `T::Assoc: 'static` assumption in the root universe and if not that it is // an error :) +// +// The pass case is also the `Or` evaluation test: rewrite can produce an ambiguous +// candidate next to a real one (`Assoc: 'static` at the root). Collapsing that `Or` +// to `Ambiguity` makes REGIONCK_ENV_PASS fail with E0283. Keep the remaining +// candidate, and keep the `Ambiguity` sibling until that candidate succeeds. #![feature(generic_const_items)] diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 1787c1912ae4f..3feb067a8c6f3 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -1,5 +1,5 @@ error: higher-ranked lifetime bound could not be satisfied - --> $DIR/alias_outlives.rs:37:45 + --> $DIR/alias_outlives.rs:42:45 | LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs b/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs index 2f0f2ca8aab85..4fc0d5ce82daf 100644 --- a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs +++ b/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs @@ -15,6 +15,9 @@ // } // rewritten to: true (via assumption) // rewritting to `for<'a, 'b> >::Assoc: 'c` would be wrong +// +// The `Or` evaluation fix is not enough for this one. Without +// `normalize_equated_region_vars` it goes ambiguous (E0283). trait Trait<'a, 'b> { type Assoc; diff --git a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr index 5e8e131addd28..3ebb61b92c5ad 100644 --- a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr +++ b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr @@ -4,10 +4,11 @@ error[E0277]: the trait bound `(): Trait fn(>::Assoc))> LL | (): Trait<>::Assoc>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` | -help: consider extending the `where` clause, but there might be an alternative better way to express this requirement +help: this trait has no implementations, consider adding one + --> $DIR/placeholder-assumptions-issue-157840.rs:3:1 | -LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> - | +++++++++++++++++++++++++++++++++++++++++++++++++ +LL | trait Trait {} + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs new file mode 100644 index 0000000000000..f3050fe336eb1 --- /dev/null +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +trait Super { + fn a(&self) { + let a: &dyn Sub = &(); + let b: &dyn Super fn(&'a ())> = a; + //~^ ERROR the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + } +} + +impl Super for () {} + +trait Sub: Super {} + +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); +} diff --git a/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr new file mode 100644 index 0000000000000..afc1f56491503 --- /dev/null +++ b/tests/ui/assumptions_on_binders/principal-upcast-region-eq-issue-157859.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + --> $DIR/principal-upcast-region-eq-issue-157859.rs:6:49 + | +LL | let b: &dyn Super fn(&'a ())> = a; + | ^ the nightly-only, unstable trait `Unsize fn(&'a ())>>` is not implemented for `dyn Sub` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn Sub` to implement `CoerceUnsized<&dyn Super fn(&'a ())>>` + = note: required for the cast from `&dyn Sub` to `&dyn Super fn(&'a ())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs new file mode 100644 index 0000000000000..d32cdd33e6e8f --- /dev/null +++ b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +trait Super { + type Assoc; + + fn a(&self) { + let a: &dyn Sub = &(); + let b: &dyn Super fn(&'a ())> = a; + //~^ ERROR the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + } +} + +impl Super for () { + type Assoc = fn(&'static ()); +} + +trait Sub: Super {} + +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); +} diff --git a/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr new file mode 100644 index 0000000000000..fb55bab0ad20c --- /dev/null +++ b/tests/ui/assumptions_on_binders/trait-upcast-projection-region-eq.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `&dyn Sub: CoerceUnsized<&dyn Super fn(&'a ())>>` is not satisfied + --> $DIR/trait-upcast-projection-region-eq.rs:8:57 + | +LL | let b: &dyn Super fn(&'a ())> = a; + | ^ the nightly-only, unstable trait `Unsize fn(&'a ())>>` is not implemented for `dyn Sub` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn Sub` to implement `CoerceUnsized<&dyn Super fn(&'a ())>>` + = note: required for the cast from `&dyn Sub` to `&dyn Super fn(&'a ())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.