Auto merge of #147791 - cuviper:beta-next, r=cuviper
[beta] backports - Change int-to-ptr transmute lowering back to inttoptr rust-lang/rust#147541 - rewrite outlives placeholder constraints to outlives static when handling opaque types rust-lang/rust#147566 - GVN: Invalidate derefs at loop headers rust-lang/rust#147607 r? cuviper
This commit is contained in:
commit
1f25197887
22 changed files with 392 additions and 106 deletions
|
|
@ -348,7 +348,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn rewrite_placeholder_outlives<'tcx>(
|
||||
pub(crate) fn rewrite_placeholder_outlives<'tcx>(
|
||||
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
annotations: &SccAnnotations<'_, '_, RegionTracker>,
|
||||
fr_static: RegionVid,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub(super) fn apply_member_constraints<'tcx>(
|
|||
debug!(?member_constraints);
|
||||
for scc_a in rcx.constraint_sccs.all_sccs() {
|
||||
debug!(?scc_a);
|
||||
// Start by adding the region values required by outlives constraints. This
|
||||
// Start by adding the region values required by outlives constraints. This
|
||||
// matches how we compute the final region values in `fn compute_regions`.
|
||||
//
|
||||
// We need to do this here to get a lower bound when applying member constraints.
|
||||
|
|
@ -64,6 +64,7 @@ fn apply_member_constraint<'tcx>(
|
|||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if !rcx.max_placeholder_universe_reached(member).is_root() {
|
||||
debug!("member region reached non root universe, bailing");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -254,6 +254,10 @@ fn collect_defining_uses<'tcx>(
|
|||
}
|
||||
} else {
|
||||
errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
|
||||
debug!(
|
||||
"collect_defining_uses: InvalidOpaqueTypeArgs for {:?} := {:?}",
|
||||
non_nll_opaque_type_key, hidden_type
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -277,6 +281,7 @@ fn collect_defining_uses<'tcx>(
|
|||
defining_uses
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(rcx, concrete_opaque_types, defining_uses, errors))]
|
||||
fn compute_concrete_types_from_defining_uses<'tcx>(
|
||||
rcx: &RegionCtxt<'_, 'tcx>,
|
||||
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
|
||||
|
|
@ -288,6 +293,7 @@ fn compute_concrete_types_from_defining_uses<'tcx>(
|
|||
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
|
||||
FxIndexMap::default();
|
||||
for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses {
|
||||
debug!(?opaque_type_key, ?arg_regions, ?hidden_type);
|
||||
// After applying member constraints, we now map all regions in the hidden type
|
||||
// to the `arg_regions` of this defining use. In case a region in the hidden type
|
||||
// ended up not being equal to any such region, we error.
|
||||
|
|
@ -295,6 +301,7 @@ fn compute_concrete_types_from_defining_uses<'tcx>(
|
|||
match hidden_type.try_fold_with(&mut ToArgRegionsFolder::new(rcx, arg_regions)) {
|
||||
Ok(hidden_type) => hidden_type,
|
||||
Err(r) => {
|
||||
debug!("UnexpectedHiddenRegion: {:?}", r);
|
||||
errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
|
||||
hidden_type,
|
||||
opaque_type_key,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ use crate::constraints::ConstraintSccIndex;
|
|||
use crate::handle_placeholders::{SccAnnotations, region_definitions};
|
||||
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
||||
use crate::region_infer::values::RegionValues;
|
||||
use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker, Representative};
|
||||
use crate::region_infer::{
|
||||
ConstraintSccs, OutlivesConstraintSet, RegionDefinition, RegionTracker, Representative,
|
||||
};
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
|
@ -39,16 +41,36 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
location_map: Rc<DenseLocationMap>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) -> RegionCtxt<'a, 'tcx> {
|
||||
let mut outlives_constraints = constraints.outlives_constraints.clone();
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let (definitions, _has_placeholders) = region_definitions(infcx, universal_regions);
|
||||
|
||||
let compute_sccs =
|
||||
|outlives_constraints: &OutlivesConstraintSet<'tcx>,
|
||||
annotations: &mut SccAnnotations<'_, 'tcx, RegionTracker>| {
|
||||
ConstraintSccs::new_with_annotation(
|
||||
&outlives_constraints
|
||||
.graph(definitions.len())
|
||||
.region_graph(outlives_constraints, universal_regions.fr_static),
|
||||
annotations,
|
||||
)
|
||||
};
|
||||
|
||||
let mut scc_annotations = SccAnnotations::init(&definitions);
|
||||
let constraint_sccs = ConstraintSccs::new_with_annotation(
|
||||
&constraints
|
||||
.outlives_constraints
|
||||
.graph(definitions.len())
|
||||
.region_graph(&constraints.outlives_constraints, universal_regions.fr_static),
|
||||
&mut scc_annotations,
|
||||
let mut constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
|
||||
|
||||
let added_constraints = crate::handle_placeholders::rewrite_placeholder_outlives(
|
||||
&constraint_sccs,
|
||||
&scc_annotations,
|
||||
universal_regions.fr_static,
|
||||
&mut outlives_constraints,
|
||||
);
|
||||
|
||||
if added_constraints {
|
||||
scc_annotations = SccAnnotations::init(&definitions);
|
||||
constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
|
||||
}
|
||||
|
||||
let scc_annotations = scc_annotations.scc_to_annotation;
|
||||
|
||||
// Unlike the `RegionInferenceContext`, we only care about free regions
|
||||
|
|
|
|||
|
|
@ -1048,14 +1048,14 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
|
||||
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
|
||||
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
|
||||
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
|
||||
(Int(..), Pointer(..)) => bx.inttoptr(imm, to_backend_ty),
|
||||
(Pointer(..), Int(..)) => {
|
||||
// FIXME: this exposes the provenance, which shouldn't be necessary.
|
||||
bx.ptrtoint(imm, to_backend_ty)
|
||||
}
|
||||
(Float(_), Pointer(..)) => {
|
||||
let int_imm = bx.bitcast(imm, bx.cx().type_isize());
|
||||
bx.ptradd(bx.const_null(bx.type_ptr()), int_imm)
|
||||
bx.inttoptr(int_imm, to_backend_ty)
|
||||
}
|
||||
(Pointer(..), Float(_)) => {
|
||||
// FIXME: this exposes the provenance, which shouldn't be necessary.
|
||||
|
|
|
|||
29
compiler/rustc_middle/src/mir/loops.rs
Normal file
29
compiler/rustc_middle/src/mir/loops.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use rustc_index::bit_set::DenseBitSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Compute the set of loop headers in the given body. A loop header is usually defined as a block
|
||||
/// which dominates one of its predecessors. This definition is only correct for reducible CFGs.
|
||||
/// However, computing dominators is expensive, so we approximate according to the post-order
|
||||
/// traversal order. A loop header for us is a block which is visited after its predecessor in
|
||||
/// post-order. This is ok as we mostly need a heuristic.
|
||||
pub fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
|
||||
let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
|
||||
let mut visited = DenseBitSet::new_empty(body.basic_blocks.len());
|
||||
for (bb, bbdata) in traversal::postorder(body) {
|
||||
// Post-order means we visit successors before the block for acyclic CFGs.
|
||||
// If the successor is not visited yet, consider it a loop header.
|
||||
for succ in bbdata.terminator().successors() {
|
||||
if !visited.contains(succ) {
|
||||
maybe_loop_headers.insert(succ);
|
||||
}
|
||||
}
|
||||
|
||||
// Only mark `bb` as visited after we checked the successors, in case we have a self-loop.
|
||||
// bb1: goto -> bb1;
|
||||
let _new = visited.insert(bb);
|
||||
debug_assert!(_new);
|
||||
}
|
||||
|
||||
maybe_loop_headers
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@ mod statement;
|
|||
mod syntax;
|
||||
mod terminator;
|
||||
|
||||
pub mod loops;
|
||||
pub mod traversal;
|
||||
pub mod visit;
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
|
|||
let ssa = SsaLocals::new(tcx, body, typing_env);
|
||||
// Clone dominators because we need them while mutating the body.
|
||||
let dominators = body.basic_blocks.dominators().clone();
|
||||
let maybe_loop_headers = loops::maybe_loop_headers(body);
|
||||
|
||||
let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
|
||||
|
||||
|
|
@ -136,6 +137,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
|
|||
|
||||
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
|
||||
for bb in reverse_postorder {
|
||||
// N.B. With loops, reverse postorder cannot produce a valid topological order.
|
||||
// A statement or terminator from inside the loop, that is not processed yet, may have performed an indirect write.
|
||||
if maybe_loop_headers.contains(bb) {
|
||||
state.invalidate_derefs();
|
||||
}
|
||||
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
|
||||
state.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
|
|||
body,
|
||||
arena,
|
||||
map: Map::new(tcx, body, Some(MAX_PLACES)),
|
||||
loop_headers: loop_headers(body),
|
||||
loop_headers: loops::maybe_loop_headers(body),
|
||||
opportunities: Vec::new(),
|
||||
};
|
||||
|
||||
|
|
@ -832,21 +832,3 @@ enum Update {
|
|||
Incr,
|
||||
Decr,
|
||||
}
|
||||
|
||||
/// Compute the set of loop headers in the given body. We define a loop header as a block which has
|
||||
/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
|
||||
/// But if the CFG is already irreducible, there is no point in trying much harder.
|
||||
/// is already irreducible.
|
||||
fn loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
|
||||
let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
// Only visit reachable blocks.
|
||||
for (bb, bbdata) in traversal::preorder(body) {
|
||||
for succ in bbdata.terminator().successors() {
|
||||
if dominators.dominates(succ, bb) {
|
||||
loop_headers.insert(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
loop_headers
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#[no_mangle]
|
||||
pub fn insert_int(x: usize) -> Result<usize, Box<()>> {
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: %[[WO_PROV:.+]] = getelementptr i8, ptr null, [[USIZE:i[0-9]+]] %x
|
||||
// CHECK-NEXT: %[[WO_PROV:.+]] = inttoptr [[USIZE:i[0-9]+]] %x to ptr
|
||||
// CHECK-NEXT: %[[R:.+]] = insertvalue { [[USIZE]], ptr } { [[USIZE]] 0, ptr poison }, ptr %[[WO_PROV]], 1
|
||||
// CHECK-NEXT: ret { [[USIZE]], ptr } %[[R]]
|
||||
Ok(x)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ fn make_ok_ptr(x: NonNull<u16>) -> Result<NonNull<u16>, usize> {
|
|||
fn make_ok_int(x: usize) -> Result<usize, NonNull<u16>> {
|
||||
// CHECK-LABEL: { i64, ptr } @make_ok_int(i64 %x)
|
||||
// CHECK-NEXT: start:
|
||||
// CHECK-NEXT: %[[NOPROV:.+]] = getelementptr i8, ptr null, i64 %x
|
||||
// CHECK-NEXT: %[[NOPROV:.+]] = inttoptr i64 %x to ptr
|
||||
// CHECK-NEXT: %[[R:.+]] = insertvalue { i64, ptr } { i64 0, ptr poison }, ptr %[[NOPROV]], 1
|
||||
// CHECK-NEXT: ret { i64, ptr } %[[R]]
|
||||
Ok(x)
|
||||
|
|
|
|||
22
tests/codegen-llvm/int-ptr-int-enum-miscompile.rs
Normal file
22
tests/codegen-llvm/int-ptr-int-enum-miscompile.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// This is a regression test for https://github.com/rust-lang/rust/issues/147265.
|
||||
|
||||
//@ compile-flags: -Copt-level=3
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn mk_result(a: usize) -> Result<u8, *const u8> {
|
||||
// CHECK-LABEL: @mk_result
|
||||
// CHECK-NOT: unreachable
|
||||
// CHECK: load i8,
|
||||
// CHECK-NOT: unreachable
|
||||
match g(a) {
|
||||
Ok(b) => Ok(unsafe { *(b as *const u8) }),
|
||||
Err(c) => Err(c),
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn g(a: usize) -> Result<usize, *const u8> {
|
||||
Ok(a)
|
||||
}
|
||||
|
|
@ -249,7 +249,7 @@ pub unsafe fn check_four_or_eight_to_nonnull(x: FourOrEight) -> NonNull<u8> {
|
|||
// OPT: call void @llvm.assume(i1 %1)
|
||||
// CHECK-NOT: icmp
|
||||
// CHECK-NOT: assume
|
||||
// CHECK: %[[RET:.+]] = getelementptr i8, ptr null, i64 %x
|
||||
// CHECK: %[[RET:.+]] = inttoptr i64 %x to ptr
|
||||
// CHECK-NEXT: ret ptr %[[RET]]
|
||||
|
||||
transmute(x)
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ pub unsafe fn check_pair_with_bool(x: (u8, bool)) -> (bool, i8) {
|
|||
pub unsafe fn check_float_to_pointer(x: f64) -> *const () {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: %0 = bitcast double %x to i64
|
||||
// CHECK: %_0 = getelementptr i8, ptr null, i64 %0
|
||||
// CHECK: %_0 = inttoptr i64 %0 to ptr
|
||||
// CHECK: ret ptr %_0
|
||||
transmute(x)
|
||||
}
|
||||
|
|
@ -378,7 +378,7 @@ pub unsafe fn check_issue_110005(x: (usize, bool)) -> Option<Box<[u8]>> {
|
|||
// CHECK-LABEL: @check_pair_to_dst_ref(
|
||||
#[no_mangle]
|
||||
pub unsafe fn check_pair_to_dst_ref<'a>(x: (usize, usize)) -> &'a [u8] {
|
||||
// CHECK: %_0.0 = getelementptr i8, ptr null, i64 %x.0
|
||||
// CHECK: %_0.0 = inttoptr i64 %x.0 to ptr
|
||||
// CHECK: %0 = icmp ne ptr %_0.0, null
|
||||
// CHECK: call void @llvm.assume(i1 %0)
|
||||
// CHECK: %1 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
|
|||
}
|
||||
|
||||
// CHECK: define{{.*}}ptr @int_to_ptr([[USIZE]] %i)
|
||||
// CHECK: %_0 = getelementptr i8, ptr null, [[USIZE]] %i
|
||||
// CHECK: %_0 = inttoptr [[USIZE]] %i to ptr
|
||||
// CHECK-NEXT: ret ptr %_0
|
||||
#[no_mangle]
|
||||
pub fn int_to_ptr(i: usize) -> *mut u16 {
|
||||
|
|
|
|||
115
tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff
Normal file
115
tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
- // MIR for `loop_deref_mut` before GVN
|
||||
+ // MIR for `loop_deref_mut` after GVN
|
||||
|
||||
fn loop_deref_mut(_1: &mut Value) -> Value {
|
||||
debug val => _1;
|
||||
let mut _0: Value;
|
||||
let _2: &Value;
|
||||
let _3: &Value;
|
||||
let mut _4: &Value;
|
||||
let mut _6: !;
|
||||
let mut _8: isize;
|
||||
let mut _9: !;
|
||||
let mut _10: ();
|
||||
let mut _12: i32;
|
||||
let _13: ();
|
||||
let mut _14: bool;
|
||||
let mut _15: !;
|
||||
let mut _16: Value;
|
||||
scope 1 {
|
||||
debug val_alias => _2;
|
||||
let mut _5: bool;
|
||||
scope 2 {
|
||||
debug stop => _5;
|
||||
let _7: i32;
|
||||
scope 3 {
|
||||
debug v => _7;
|
||||
let _11: Value;
|
||||
scope 4 {
|
||||
debug v => _11;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
StorageLive(_4);
|
||||
_4 = &(*_1);
|
||||
_3 = get::<Value>(move _4) -> [return: bb1, unwind unreachable];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_2 = &(*_3);
|
||||
StorageDead(_4);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
StorageLive(_5);
|
||||
_5 = const false;
|
||||
- _8 = discriminant((*_2));
|
||||
+ _8 = discriminant((*_3));
|
||||
switchInt(move _8) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb2: {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
- StorageLive(_7);
|
||||
- _7 = copy (((*_2) as V0).0: i32);
|
||||
+ nop;
|
||||
+ _7 = copy (((*_3) as V0).0: i32);
|
||||
StorageLive(_9);
|
||||
goto -> bb4;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
StorageLive(_11);
|
||||
StorageLive(_12);
|
||||
_12 = copy _7;
|
||||
- _11 = Value::V0(move _12);
|
||||
+ _11 = Value::V0(copy _7);
|
||||
StorageDead(_12);
|
||||
StorageLive(_13);
|
||||
StorageLive(_14);
|
||||
_14 = copy _5;
|
||||
switchInt(move _14) -> [0: bb6, otherwise: bb5];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
_0 = move _11;
|
||||
StorageDead(_14);
|
||||
StorageDead(_13);
|
||||
StorageDead(_11);
|
||||
StorageDead(_9);
|
||||
- StorageDead(_7);
|
||||
+ nop;
|
||||
StorageDead(_5);
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
|
||||
bb6: {
|
||||
_13 = const ();
|
||||
StorageDead(_14);
|
||||
StorageDead(_13);
|
||||
_5 = const true;
|
||||
StorageLive(_16);
|
||||
- _16 = Value::V1;
|
||||
- (*_1) = move _16;
|
||||
+ _16 = const Value::V1;
|
||||
+ (*_1) = const Value::V1;
|
||||
StorageDead(_16);
|
||||
_10 = const ();
|
||||
StorageDead(_11);
|
||||
goto -> bb4;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ ALLOC0 (size: 8, align: 4) {
|
||||
+ 01 00 00 00 __ __ __ __ │ ....░░░░
|
||||
}
|
||||
|
||||
39
tests/mir-opt/gvn_loop.rs
Normal file
39
tests/mir-opt/gvn_loop.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//@ test-mir-pass: GVN
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(core_intrinsics, rustc_attrs)]
|
||||
|
||||
pub enum Value {
|
||||
V0(i32),
|
||||
V1,
|
||||
}
|
||||
|
||||
// Check that we do not use the dereferenced value of `val_alias` when returning.
|
||||
|
||||
// EMIT_MIR gvn_loop.loop_deref_mut.GVN.diff
|
||||
fn loop_deref_mut(val: &mut Value) -> Value {
|
||||
// CHECK-LABEL: fn loop_deref_mut(
|
||||
// CHECK: [[VAL_REF:_.*]] = get::<Value>(
|
||||
// CHECK: [[V:_.*]] = copy (((*[[VAL_REF]]) as V0).0: i32);
|
||||
// CEHCK-NOT: copy (*[[VAL_REF]]);
|
||||
// CHECK: [[RET:_*]] = Value::V0(copy [[V]]);
|
||||
// CEHCK-NOT: copy (*[[VAL_REF]]);
|
||||
// CHECK: _0 = move [[RET]]
|
||||
let val_alias: &Value = get(val);
|
||||
let mut stop = false;
|
||||
let Value::V0(v) = *val_alias else { unsafe { core::intrinsics::unreachable() } };
|
||||
loop {
|
||||
let v = Value::V0(v);
|
||||
if stop {
|
||||
return v;
|
||||
}
|
||||
stop = true;
|
||||
*val = Value::V1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[rustc_nounwind]
|
||||
fn get<T>(v: &T) -> &T {
|
||||
v
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//@ check-pass
|
||||
|
||||
// We have some `RPIT` with an item bound of `for<'a> Outlives<'a>`. We
|
||||
// infer a hidden type of `&'?x i32` where `'?x` is required to outlive
|
||||
// some placeholder `'!a` due to the `for<'a> Outlives<'a>` item bound.
|
||||
//
|
||||
// We previously did not write constraints of the form `'?x: '!a` into
|
||||
// `'?x: 'static`. This caused member constraints to bail and not consider
|
||||
// `'?x` to be constrained to an arg region.
|
||||
|
||||
pub trait Outlives<'a> {}
|
||||
impl<'a, T: 'a> Outlives<'a> for T {}
|
||||
|
||||
pub fn foo() -> impl for<'a> Outlives<'a> {
|
||||
let x: &'static i32 = &1;
|
||||
x
|
||||
}
|
||||
|
||||
// This *didn't* regress but feels like it's "the same thing" so
|
||||
// test it anyway
|
||||
pub fn bar() -> impl Sized {
|
||||
let x: &'static i32 = &1;
|
||||
hr_outlives(x)
|
||||
}
|
||||
|
||||
fn hr_outlives<T>(v: T) -> T
|
||||
where
|
||||
for<'a> T: 'a
|
||||
{
|
||||
v
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -48,7 +48,6 @@ fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = im
|
|||
// This should resolve.
|
||||
fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
|
||||
//~^ ERROR implementation of `Bar` is not general enough
|
||||
//~| ERROR lifetime may not live long enough
|
||||
|
||||
// This should resolve.
|
||||
fn two_htrb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Qux<'b>> {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error[E0261]: use of undeclared lifetime name `'b`
|
||||
--> $DIR/nested-rpit-hrtb.rs:57:77
|
||||
--> $DIR/nested-rpit-hrtb.rs:56:77
|
||||
|
|
||||
LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
|
||||
| ^^ undeclared lifetime
|
||||
|
|
@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz
|
|||
| ++++
|
||||
|
||||
error[E0261]: use of undeclared lifetime name `'b`
|
||||
--> $DIR/nested-rpit-hrtb.rs:65:82
|
||||
--> $DIR/nested-rpit-hrtb.rs:64:82
|
||||
|
|
||||
LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
|
||||
| ^^ undeclared lifetime
|
||||
|
|
@ -87,12 +87,6 @@ LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc
|
|||
but trait `Qux<'_>` is implemented for `()`
|
||||
= help: for that trait implementation, expected `()`, found `&'a ()`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/nested-rpit-hrtb.rs:49:93
|
||||
|
|
||||
LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
|
||||
| -- lifetime `'b` defined here ^^ opaque type requires that `'b` must outlive `'static`
|
||||
|
||||
error: implementation of `Bar` is not general enough
|
||||
--> $DIR/nested-rpit-hrtb.rs:49:93
|
||||
|
|
||||
|
|
@ -103,7 +97,7 @@ LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc =
|
|||
= note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0`
|
||||
|
||||
error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied
|
||||
--> $DIR/nested-rpit-hrtb.rs:61:64
|
||||
--> $DIR/nested-rpit-hrtb.rs:60:64
|
||||
|
|
||||
LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()`
|
||||
|
|
@ -112,7 +106,7 @@ LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b>
|
|||
but trait `Qux<'_>` is implemented for `()`
|
||||
= help: for that trait implementation, expected `()`, found `&'a ()`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0261, E0277, E0657.
|
||||
For more information about an error, try `rustc --explain E0261`.
|
||||
|
|
|
|||
34
tests/ui/mir/gvn-loop-miscompile.rs
Normal file
34
tests/ui/mir/gvn-loop-miscompile.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//@ compile-flags: -O
|
||||
//@ run-pass
|
||||
|
||||
pub enum Value {
|
||||
V0(i32),
|
||||
V1,
|
||||
}
|
||||
|
||||
fn set_discriminant(val: &mut Value) -> Value {
|
||||
let val_alias: &Value = get(val);
|
||||
let mut stop = false;
|
||||
let Value::V0(v) = *val_alias else {
|
||||
unreachable!();
|
||||
};
|
||||
loop {
|
||||
let v = Value::V0(v);
|
||||
if stop {
|
||||
return v;
|
||||
}
|
||||
stop = true;
|
||||
*val = Value::V1;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut v = Value::V0(1);
|
||||
let v = set_discriminant(&mut v);
|
||||
assert!(matches!(v, Value::V0(1)));
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn get<T>(v: &T) -> &T {
|
||||
v
|
||||
}
|
||||
|
|
@ -17,75 +17,77 @@ LL | A(B(C::new(D::new(move |st| f(st)))))
|
|||
= note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:5
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:5
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:41
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue