Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow constants to refer statics in const eval #71332

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions src/librustc_mir/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,20 +366,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
);
}
}
} else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this diff doing here? This is changing the borrow checker, which should be unnecessary when all you want to do is change the dynamic const-to-static check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I should have been clearer in the commit message. This check assumes that the only constants that can point to statics are the ones created and immediately dereferenced during MIR building, so it fails with anything else. (For example, a constant pointing to a static's field, which has a different type than the static itself.)

So the first commit of this PR replaces this check with a more precise one suggested (AFAIU) by @eddyb when it was added. I guess this could also make sense as a separate PR? Or at least some borrowck-oriented review?

Copy link
Member

@RalfJung RalfJung Apr 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So without this, the rest of the PR fails, or so?

I certainly cannot review anything in borrw_check or mir_build, I have never touched or read any of that code.^^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, without this many scenarios using consts that refer to statics will fail at that span_mirbug!.

let unnormalized_ty = tcx.type_of(static_def_id);
let locations = location.to_locations();
let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty;

if let Err(terr) = self.cx.eq_types(
normalized_ty,
literal_ty,
locations,
ConstraintCategory::Boring,
) {
span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr);
}
}

if let ty::FnDef(def_id, substs) = constant.literal.ty.kind {
Expand Down
56 changes: 33 additions & 23 deletions src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra};
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind,
InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar,
ScalarMaybeUndef, StackPopCleanup,
intern_const_alloc_recursive, AllocId, Allocation, ConstValue, GlobalAlloc, GlobalId,
Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst,
RefTracking, Scalar, ScalarMaybeUndef, StackPopCleanup,
};
use rustc_hir::def::DefKind;
use rustc_middle::mir;
Expand Down Expand Up @@ -94,10 +94,13 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
)
}

pub(super) fn op_to_const<'tcx>(
#[derive(Debug)]
pub(super) struct RefersToStatic;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need a new error type here. There are tons of errors that can happen in const-eval, why is this one so special that it has to shop up here? There is no precedent for anything like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually an error. This is a signal that we should turn this into a ByRef constant instead of trying to use Scalar or Slice. At the very least it must be documented to explain this properly, but imo you an also just use Option as the return type of try_op_to_const


pub(super) fn try_op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, 'tcx>,
op: OpTy<'tcx>,
) -> ConstValue<'tcx> {
) -> Result<ConstValue<'tcx>, RefersToStatic> {
// We do not have value optimizations for everything.
// Only scalars and slices, since they are very common.
// Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
Expand Down Expand Up @@ -128,10 +131,16 @@ pub(super) fn op_to_const<'tcx>(
op.try_as_mplace(ecx)
};

let alloc_map = ecx.tcx.alloc_map.lock();
let try_unwrap_memory = |alloc_id: AllocId| match alloc_map.get(alloc_id) {
Some(GlobalAlloc::Memory(mem)) => Ok(mem),
Some(GlobalAlloc::Static(_)) => Err(RefersToStatic),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks our assumption, that any constant of scalar type or &str/&[u8] type is ConstValue::Scalar or ConstValue::Slice respectively. If such a constant is used in pattern matching, things will break in bad ways (e.g. two identical patterns not comparing equal). I guess such constants must be forbidden from occurring in patterns and const generics.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... pattern matching assumes that references in consts can only point to immutable memory? That's... a rather big assumption! It was true so far but only due to extreme conservatism on the side of our static const checks.

However, I feel like the fact that &str is an immutable shared reference should count for something. Maybe interning should ensure that shared references only point to read-only allocations, or so? (recursively)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the &str came from a &mut str, we can't turn the &mut str allocation into an immutable one. And if we allow constants to refer to statics, that can happen, and we must keep the pointer to the static, because otherwise we violate the fact that a pointer to the same static must have the same address.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How'd that look like though? When a const takes an &mut to something, that reference has lifetime 'static, so nothing else ever in the entire program is allowed to access the memory behind that unique reference.

Though when we have &i32, that could indeed be from an &mut i32 that only covers part of an allocation and the rest of it could still permit mutation.

I think allowing references in consts in patterns was a big mistake... do we permit this for all types or just &str/&[u8]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think allowing references in consts in patterns was a big mistake... do we permit this for all types or just &str/&[u8]?

Hold up :D we need special rules for pattern constants and const generics anyway. @eddyb talked to me about this already. E.g. we can't non-normalized padding or non-interned references in constants used in const generics. We do need a separate interning & normalization for such constants anyway. Not sure how we can detect references into statics though, maybe we need a new flag in Alloation that marks the allocation as part of a static, and thus having a unique address.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened rust-lang/const-eval#42 for the soundness concerns.

We probably have to get that answered before allowing consts pointing to mutable statics in our dynamic checks. Sorry @rpjohnst, I was entirely unaware of this particular minefield.

For now, could you remove those changes from this PR? I.e., dynamically permit consts pointing to immutable statics, but make no effort to permit consts pointing to mutable statics?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we do it behind a feature gate, that's perfectly fine in my book.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rpjohnst's use-case doesn't need consts pointing to mutable statics though. I think this change is part of this PR because I recommended them to do that, because I thought we could weaken our dynamic checks all the way to "just prevent reads from mutable statics, everything else is fine".

I was wrong as I didn't know about pattern-matching-soundness. In light of this new information, I'd prefer it we didn't weaken our dynamic checks quite so far.

Copy link
Contributor Author

@rpjohnst rpjohnst Apr 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will do. To clarify- do we want these dynamic checks behind a feature gate as well? Or can we rely on the static check and just feature gate any changes there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you avoid touching validity (beyond what is necessary to enable consts to point to immutable statics), I don't think any feature gates are needed for weakening the dynamic checks.

_ => bug!("expected allocation ID {} to point to memory", alloc_id),
};
let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr {
Scalar::Ptr(ptr) => {
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
ConstValue::ByRef { alloc, offset: ptr.offset }
let alloc = try_unwrap_memory(ptr.alloc_id)?;
Ok(ConstValue::ByRef { alloc, offset: ptr.offset })
}
Scalar::Raw { data, .. } => {
assert!(mplace.layout.is_zst());
Expand All @@ -141,22 +150,20 @@ pub(super) fn op_to_const<'tcx>(
"this MPlaceTy must come from `try_as_mplace` being used on a zst, so we know what
value this integer address must have",
);
ConstValue::Scalar(Scalar::zst())
Ok(ConstValue::Scalar(Scalar::zst()))
}
};
match immediate {
Ok(mplace) => to_const_value(mplace),
Ok(mplace) => Ok(to_const_value(mplace)?),
// see comment on `let try_as_immediate` above
Err(imm) => match *imm {
Immediate::Scalar(x) => match x {
ScalarMaybeUndef::Scalar(s) => ConstValue::Scalar(s),
ScalarMaybeUndef::Undef => to_const_value(op.assert_mem_place(ecx)),
ScalarMaybeUndef::Scalar(s) => Ok(ConstValue::Scalar(s)),
ScalarMaybeUndef::Undef => Ok(to_const_value(op.assert_mem_place(ecx))?),
},
Immediate::ScalarPair(a, b) => {
let (data, start) = match a.not_undef().unwrap() {
Scalar::Ptr(ptr) => {
(ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), ptr.offset.bytes())
}
Scalar::Ptr(ptr) => (try_unwrap_memory(ptr.alloc_id)?, ptr.offset.bytes()),
Scalar::Raw { .. } => (
ecx.tcx
.intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])),
Expand All @@ -166,7 +173,7 @@ pub(super) fn op_to_const<'tcx>(
let len = b.to_machine_usize(&ecx.tcx.tcx).unwrap();
let start = start.try_into().unwrap();
let len: usize = len.try_into().unwrap();
ConstValue::Slice { data, start, end: start + len }
Ok(ConstValue::Slice { data, start, end: start + len })
}
},
}
Expand Down Expand Up @@ -198,17 +205,20 @@ fn validate_and_turn_into_const<'tcx>(
}
}
// Now that we validated, turn this into a proper constant.
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
// whether they become immediates.
if is_static || cid.promoted.is_some() {
// Statics/promoteds are always `ByRef`, for the rest `try_op_to_const`
// decides whether they become immediates.
let value = if !is_static && !cid.promoted.is_some() {
try_op_to_const(&ecx, mplace.into())
} else {
Err(RefersToStatic)
};
Ok(value.unwrap_or_else(|RefersToStatic| {
let ptr = mplace.ptr.assert_ptr();
Ok(ConstValue::ByRef {
ConstValue::ByRef {
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
})
} else {
Ok(op_to_const(&ecx, mplace.into()))
}
}
}))
})();

val.map_err(|error| {
Expand Down
12 changes: 4 additions & 8 deletions src/librustc_mir/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::hash::Hash;
use rustc_data_structures::fx::FxHashMap;

use rustc_ast::ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::AssertMessage;
use rustc_span::symbol::Symbol;

Expand Down Expand Up @@ -353,7 +352,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
memory_extra: &MemoryExtra,
alloc_id: AllocId,
allocation: &Allocation,
static_def_id: Option<DefId>,
is_write: bool,
) -> InterpResult<'tcx> {
if is_write {
Expand All @@ -368,16 +366,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
if memory_extra.can_access_statics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be rename to can_access_mutable_globals or so.

// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
} else if static_def_id.is_some() {
// Machine configuration does not allow us to read statics
} else if allocation.mutability != Mutability::Not {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if allocation.mutability != Mutability::Not {
} else if allocation.mutability == Mutability::Mut {

// Machine configuration does not allow us to read mutable statics
// (e.g., `const` initializer).
// This is unsound because the content of this allocation may be different now and
// at run-time, so if we permit reading it now we might return the wrong value.
Err(ConstEvalErrKind::ConstAccessesStatic.into())
} else {
// Immutable global, this read is fine.
// But make sure we never accept a read from something mutable, that would be
// unsound. The reason is that as the content of this allocation may be different
// now and at run-time, so if we permit reading now we might return the wrong value.
assert_eq!(allocation.mutability, Mutability::Not);
Ok(())
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/const_eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub(crate) fn const_field<'tcx>(
let field = ecx.operand_field(down, field.index()).unwrap();
// and finally move back to the const world, always normalizing because
// this is not called for statics.
op_to_const(&ecx, field)
try_op_to_const(&ecx, field).unwrap()
}

pub(crate) fn const_caller_location(
Expand Down Expand Up @@ -81,7 +81,7 @@ pub(crate) fn destructure_const<'tcx>(
let down = ecx.operand_downcast(op, variant).unwrap();
let fields_iter = (0..field_count).map(|i| {
let field_op = ecx.operand_field(down, i).unwrap();
let val = op_to_const(&ecx, field_op);
let val = try_op_to_const(&ecx, field_op).unwrap();
ty::Const::from_value(tcx, val, field_op.layout.ty)
});
let fields = tcx.arena.alloc_from_iter(fields_iter);
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_mir/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::hash::Hash;

use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
use rustc_span::def_id::DefId;

use super::{
AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind,
Expand Down Expand Up @@ -207,13 +206,11 @@ pub trait Machine<'mir, 'tcx>: Sized {
}

/// Called before a global allocation is accessed.
/// `def_id` is `Some` if this is the "lazy" allocation of a static.
#[inline]
fn before_access_global(
_memory_extra: &Self::MemoryExtra,
_alloc_id: AllocId,
_allocation: &Allocation,
_static_def_id: Option<DefId>,
_is_write: bool,
) -> InterpResult<'tcx> {
Ok(())
Expand Down
14 changes: 5 additions & 9 deletions src/librustc_mir/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
is_write: bool,
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
let alloc = tcx.alloc_map.lock().get(id);
let (alloc, def_id) = match alloc {
Some(GlobalAlloc::Memory(mem)) => {
// Memory of a constant or promoted or anonymous memory referenced by a static.
(mem, None)
}
let alloc = match alloc {
// Memory of a constant or promoted or anonymous memory referenced by a static.
Some(GlobalAlloc::Memory(mem)) => mem,
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
None => throw_ub!(PointerUseAfterFree(id)),
Some(GlobalAlloc::Static(def_id)) => {
Expand Down Expand Up @@ -466,12 +464,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
})?;
// Make sure we use the ID of the resolved memory, not the lazy one!
let id = raw_const.alloc_id;
let allocation = tcx.alloc_map.lock().unwrap_memory(id);

(allocation, Some(def_id))
tcx.alloc_map.lock().unwrap_memory(id)
}
};
M::before_access_global(memory_extra, id, alloc, def_id, is_write)?;
M::before_access_global(memory_extra, id, alloc, is_write)?;
let alloc = Cow::Borrowed(alloc);
// We got tcx memory. Let the machine initialize its "extra" stuff.
let (alloc, tag) = M::init_allocation_extra(
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_mir/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
return Ok(());
}
// FIXME: Statics referenced from consts are skipped.
// This avoids spurious "const accesses static" errors
// unrelated to validity, but is similarly unsound.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd merge this with the if above, to

if !did.is_local() || self.ecx.tcx.is_static(did) {

The comment should also be updated:

// We don't re-validate statics because 
// - the const-machine we are in might forbid reading from them, and
// - they have already been validated.
// FIXME: validation might have happened at a different type, but for now
// we consider this an okay trade-off.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But your check is stricter in the sense that it validates more, so maybe @oli-obk would prefer that one.

I just think it's generally not worth recursing into other globals -- this can only possibly catch a bug if people did some crazy unsafe stuff, and those people asked for it when they get post-monomorphization errors. So I'd actually be fine to just always bail for any Some(GlobalAlloc::Static(_)) here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the stricter check because there are tests for this, for statics-pointing-to-statics:

Happy to collapse it into the first check if we're okay ditching those tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it is okay to not catch the failure in double_check2. We already do not catch it cross-crate. But let's wait what @oli-obk thinks -- and in fact let's Cc @rust-lang/wg-const-eval and see what everyone else thinks :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, how do we not catch this cross crate? Don't we revisit statics from other crates if we encounter them at a different type of non-zero offset?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we don't, as far as I know.

if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
throw_validation_failure!(
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
format_args!("a {} pointing to a static variable", kind),
self.path
);
return Ok(());
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::{def_id::DefId, Span};
use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout};
use rustc_trait_selection::traits;

Expand Down Expand Up @@ -274,7 +274,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
_memory_extra: &(),
_alloc_id: AllocId,
allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
_static_def_id: Option<DefId>,
is_write: bool,
) -> InterpResult<'tcx> {
if is_write {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir_build/build/expr/as_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let Expr { ty, temp_lifetime: _, span, kind } = expr;
match kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
ExprKind::Literal { literal, user_ty } => {
ExprKind::Literal { literal, user_ty } |
ExprKind::StaticRef { literal, user_ty, .. } => {
rpjohnst marked this conversation as resolved.
Show resolved Hide resolved
let user_ty = user_ty.map(|user_ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span,
Expand All @@ -32,7 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
assert_eq!(literal.ty, ty);
Constant { span, user_ty, literal }
}
ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal },
_ => span_bug!(span, "expression is not a valid constant {:?}", kind),
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/librustc_mir_build/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::hair::*;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_index::vec::Idx;
use rustc_infer::infer::RegionVariableOrigin;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::BorrowKind;
use rustc_middle::ty::adjustment::{
Expand Down Expand Up @@ -742,13 +743,26 @@ fn convert_path_expr<'a, 'tcx>(
let ty = cx.tcx.static_ptr_ty(id);
let ptr = cx.tcx.alloc_map.lock().create_static_alloc(id);
let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);

// Keep regions around in a user type annotation for MIR typeck:
let static_ty = cx.tcx.type_of(id);
let static_ptr_ty = if cx.tcx.is_mutable_static(id) {
cx.tcx.mk_mut_ptr(static_ty)
} else {
let origin = RegionVariableOrigin::AddrOfRegion(expr.span);
let region = cx.infcx.next_region_var(origin);
cx.tcx.mk_imm_ref(region, static_ty)
};
let user_ty = cx.infcx.canonicalize_user_type_annotation(&UserType::Ty(static_ptr_ty));

ExprKind::Deref {
arg: Expr {
ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::StaticRef {
literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty),
user_ty: Some(user_ty),
def_id: id,
},
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir_build/hair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ crate enum ExprKind<'tcx> {
/// info for diagnostics.
StaticRef {
literal: &'tcx Const<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
def_id: DefId,
},
LlvmInlineAsm {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ promoted[0] in BAR: &[&i32; 1] = {
// + val: Value(Scalar(alloc0+0))
// mir::Constant
// + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
// + user_ty: UserType(0)
// + literal: Const { ty: &i32, val: Value(Scalar(alloc0+0)) }
_2 = _3; // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
_1 = [move _2]; // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
- // MIR for `BAR` before PromoteTemps
+ // MIR for `BAR` after PromoteTemps

| User Type Annotations
| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(&i32) } at $DIR/const-promotion-extern-static.rs:9:33: 9:34
|
static mut BAR: *const &i32 = {
let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28
let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
Expand All @@ -25,6 +28,7 @@
+ // + val: Unevaluated(DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), [], Some(promoted[0]))
// mir::Constant
- // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
- // + user_ty: UserType(0)
- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0+0)) }
- _4 = &(*_5); // bb0[6]: scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
- _3 = [move _4]; // bb0[7]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ promoted[0] in FOO: &[&i32; 1] = {
// + val: Value(Scalar(alloc2+0))
// mir::Constant
// + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
// + user_ty: UserType(0)
// + literal: Const { ty: &i32, val: Value(Scalar(alloc2+0)) }
_2 = _3; // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
_1 = [move _2]; // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
- // MIR for `FOO` before PromoteTemps
+ // MIR for `FOO` after PromoteTemps

| User Type Annotations
| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(&i32) } at $DIR/const-promotion-extern-static.rs:13:42: 13:43
|
static mut FOO: *const &i32 = {
let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28
let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
Expand All @@ -27,6 +30,7 @@
+ // + val: Unevaluated(DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), [], Some(promoted[0]))
// mir::Constant
- // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
- // + user_ty: UserType(0)
- // + literal: Const { ty: &i32, val: Value(Scalar(alloc2+0)) }
- _4 = &(*_5); // bb0[6]: scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
- _3 = [move _4]; // bb0[7]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn main() -> () {
// + val: Value(Scalar(alloc0+0))
// mir::Constant
// + span: $DIR/const_allocation.rs:8:5: 8:8
// + user_ty: UserType(0)
// + literal: Const { ty: &&[(std::option::Option<i32>, &[&str])], val: Value(Scalar(alloc0+0)) }
_1 = (*_2); // bb0[3]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
StorageDead(_2); // bb0[4]: scope 0 at $DIR/const_allocation.rs:8:8: 8:9
Expand Down
Loading