Skip to content

Commit

Permalink
Auto merge of #116564 - oli-obk:evaluated_static_in_metadata, r=<try>
Browse files Browse the repository at this point in the history
Store static initializers in metadata instead of the MIR of statics.

This means that adding generic statics would be even more difficult, as we can't evaluate statics from other crates anymore, but the subtle issues I have encountered make me think that having this be an explicit problem is better. Said issues are:

### Nested allocations of static items get duplicated

which leads to issues for statics like

```rust
static mut FOO: &mut u32 = &mut 42;
static mut BAR = unsafe { FOO };
```

getting different allocations, instead of referring to the same one. This is also true for non-static mut, but promotion makes `static FOO: &u32 = &42;` annoying to demo.

### The main allocation of a static gets duplicated across crates

```rust
// crate a

static mut FOO: Option<u32> = Some(42);

// crate b

static mut BAR: &mut u32 = unsafe {
    match &mut a::FOO {
        Some(x) => x,
        None => panic!(),
    }
};
```

## Why is this being done?

In order to ensure all crates see the same nested allocations (which is the last issue that needs fixing before we can stabilize [`const_mut_refs`](#57349)), I am working on creating anonymous (from the Rust side, to LLVM it's like a regular static item) static items for the nested allocations in a static. If we evaluate the static item in a downstream crate again, we will end up duplicating its nested allocations (and in some cases, like the `match` case, even duplicate the main allocation).
  • Loading branch information
bors committed Oct 9, 2023
2 parents 7ed044c + 7a7456d commit ca76eb8
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 42 deletions.
7 changes: 7 additions & 0 deletions compiler/rustc_const_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ pub fn provide(providers: &mut Providers) {
const_eval::provide(providers);
providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
providers.eval_static_initializer_raw = |tcx, def_id| {
assert!(tcx.is_static(def_id.to_def_id()));
let instance = ty::Instance::mono(tcx, def_id.to_def_id());
let gid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
let param_env = ty::ParamEnv::reveal_all();
Ok(tcx.eval_to_allocation_raw(param_env.and(gid))?.alloc_id)
};
providers.const_caller_location = const_eval::const_caller_location;
providers.eval_to_valtree = |tcx, param_env_and_value| {
let (param_env, raw) = param_env_and_value.into_parts();
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ provide! { tcx, def_id, other, cdata,
asyncness => { table_direct }
fn_arg_names => { table }
generator_kind => { table }
eval_static_initializer_raw => {
Ok(cdata
.root
.tables
.eval_static_initializer_raw
.get(cdata, def_id.index)
.map(|lazy| lazy.decode((cdata, tcx)))
.unwrap_or_else(|| panic!("{def_id:?} does not have eval_static_initializer_raw")))
}
trait_def => { table }
deduced_param_attrs => { table }
is_type_alias_impl_trait => {
Expand Down
14 changes: 9 additions & 5 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1034,11 +1034,9 @@ fn should_encode_mir(
(true, mir_opt_base)
}
// Constants
DefKind::AnonConst
| DefKind::InlineConst
| DefKind::AssocConst
| DefKind::Static(..)
| DefKind::Const => (true, false),
DefKind::AnonConst | DefKind::InlineConst | DefKind::AssocConst | DefKind::Const => {
(true, false)
}
// Full-fledged functions + closures
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
let generics = tcx.generics_of(def_id);
Expand Down Expand Up @@ -1439,6 +1437,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let data = self.tcx.generator_kind(def_id).unwrap();
record!(self.tables.generator_kind[def_id] <- data);
}
if let DefKind::Static(_) = def_kind {
if !self.tcx.is_foreign_item(def_id) {
let data = self.tcx.eval_static_initializer_raw(def_id).unwrap();
record!(self.tables.eval_static_initializer_raw[def_id] <- data);
}
}
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
self.encode_info_for_adt(local_id);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ define_tables! {
asyncness: Table<DefIndex, ty::Asyncness>,
fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
eval_static_initializer_raw: Table<DefIndex, LazyValue<mir::interpret::AllocId>>,
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
trait_item_def_id: Table<DefIndex, RawDefId>,
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
TrivialTypeTraversalImpls! { ErrorHandled }

pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalStaticInitializerRawResult = Result<AllocId, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed.
/// This is needed in `thir::pattern::lower_inline_const`.
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ use crate::ty::GenericArgKind;
use crate::ty::{self, Instance, Ty, TyCtxt};

pub use self::error::{
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, PointerKind,
ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult,
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo,
MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch,
UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
};

pub use self::value::Scalar;
Expand Down
24 changes: 3 additions & 21 deletions compiler/rustc_middle/src/mir/interpret/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,8 @@ impl<'tcx> TyCtxtAt<'tcx> {
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
trace!("eval_static_initializer: Need to compute {:?}", def_id);
assert!(self.is_static(def_id));
let instance = ty::Instance::mono(*self, def_id);
let gid = GlobalId { instance, promoted: None };
self.eval_to_allocation(gid, ty::ParamEnv::reveal_all())
}

/// Evaluate anything constant-like, returning the allocation of the final memory.
///
/// The span is entirely ignored here, but still helpful for better query cycle errors.
fn eval_to_allocation(
self,
gid: GlobalId<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
trace!("eval_to_allocation: Need to compute {:?}", gid);
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
let alloc_id = self.eval_static_initializer_raw(def_id)?;
Ok(self.global_alloc(alloc_id).unwrap_memory())
}
}

Expand Down Expand Up @@ -237,10 +223,6 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
pub fn eval_static_initializer(self, def_id: DefId) {
trace!("eval_static_initializer: Need to compute {:?}", def_id);
assert!(self.tcx.is_static(def_id));
let instance = ty::Instance::mono(self.tcx, def_id);
let gid = GlobalId { instance, promoted: None };
let param_env = ty::ParamEnv::reveal_all();
trace!("eval_to_allocation: Need to compute {:?}", gid);
self.eval_to_allocation_raw(param_env.and(gid))
self.eval_static_initializer_raw(def_id);
}
}
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ trivial! {
rustc_middle::mir::interpret::AllocId,
rustc_middle::mir::interpret::ErrorHandled,
rustc_middle::mir::interpret::LitToConstError,
rustc_middle::mir::interpret::EvalStaticInitializerRawResult,
rustc_middle::thir::ExprId,
rustc_middle::traits::CodegenObligationError,
rustc_middle::traits::EvaluationResult,
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use crate::middle::stability::{self, DeprecationEntry};
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::{
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult,
EvalToValTreeResult,
};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
Expand Down Expand Up @@ -1073,6 +1074,16 @@ rustc_queries! {
cache_on_disk_if { true }
}

/// Evaluate a static's initializer, returning the allocation of the initializer's memory.
query eval_static_initializer_raw(key: DefId) -> EvalStaticInitializerRawResult {
desc { |tcx|
"evaluating initializer of static `{}`",
tcx.def_path_str(key)
}
cache_on_disk_if { true }
separate_provide_extern
}

/// Evaluates const items or anonymous constants
/// (such as enum variant explicit discriminants or array lengths)
/// into a representation suitable for the type system and const generics.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/parameterized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ trivially_parameterized_over_tcx! {
crate::middle::exported_symbols::SymbolExportInfo,
crate::middle::resolve_bound_vars::ObjectLifetimeDefault,
crate::mir::ConstQualifs,
crate::mir::interpret::AllocId,
ty::AssocItemContainer,
ty::Asyncness,
ty::DeducedParamAttrs,
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/consts/recursive-zst-static.default.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
error[E0391]: cycle detected when const-evaluating + checking `FOO`
error[E0391]: cycle detected when evaluating initializer of static `FOO`
--> $DIR/recursive-zst-static.rs:10:1
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:1
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:18
|
LL | static FOO: () = FOO;
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: ...which again requires evaluating initializer of static `FOO`, completing the cycle
note: cycle used when linting top-level module
--> $DIR/recursive-zst-static.rs:10:1
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/recursive-zst-static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// can depend on this fact and will thus do unsound things when it is violated.
// See https://github.com/rust-lang/rust/issues/71078 for more details.

static FOO: () = FOO; //~ cycle detected when const-evaluating + checking `FOO`
static FOO: () = FOO; //~ cycle detected when evaluating initializer of static `FOO`

fn main() {
FOO
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/consts/recursive-zst-static.unleash.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
error[E0391]: cycle detected when const-evaluating + checking `FOO`
error[E0391]: cycle detected when evaluating initializer of static `FOO`
--> $DIR/recursive-zst-static.rs:10:1
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:1
|
LL | static FOO: () = FOO;
| ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-zst-static.rs:10:18
|
LL | static FOO: () = FOO;
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: ...which again requires evaluating initializer of static `FOO`, completing the cycle
note: cycle used when linting top-level module
--> $DIR/recursive-zst-static.rs:10:1
|
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/consts/write-to-static-mut-in-static.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ error[E0080]: could not evaluate static initializer
LL | pub static mut B: () = unsafe { A = 1; };
| ^^^^^ modifying a static's initial value from another static's initializer

error[E0391]: cycle detected when const-evaluating + checking `C`
error[E0391]: cycle detected when evaluating initializer of static `C`
--> $DIR/write-to-static-mut-in-static.rs:5:1
|
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
| ^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `C`...
--> $DIR/write-to-static-mut-in-static.rs:5:1
|
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
| ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `C`...
--> $DIR/write-to-static-mut-in-static.rs:5:34
|
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
| ^^^^^
= note: ...which again requires const-evaluating + checking `C`, completing the cycle
= note: ...which again requires evaluating initializer of static `C`, completing the cycle
note: cycle used when linting top-level module
--> $DIR/write-to-static-mut-in-static.rs:1:1
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/recursion/recursive-static-definition.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub static FOO: u32 = FOO;
//~^ ERROR cycle detected when const-evaluating + checking `FOO`
//~^ ERROR cycle detected when evaluating initializer of static `FOO`

fn main() {}
9 changes: 7 additions & 2 deletions tests/ui/recursion/recursive-static-definition.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
error[E0391]: cycle detected when const-evaluating + checking `FOO`
error[E0391]: cycle detected when evaluating initializer of static `FOO`
--> $DIR/recursive-static-definition.rs:1:1
|
LL | pub static FOO: u32 = FOO;
| ^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-static-definition.rs:1:1
|
LL | pub static FOO: u32 = FOO;
| ^^^^^^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/recursive-static-definition.rs:1:23
|
LL | pub static FOO: u32 = FOO;
| ^^^
= note: ...which again requires const-evaluating + checking `FOO`, completing the cycle
= note: ...which again requires evaluating initializer of static `FOO`, completing the cycle
note: cycle used when linting top-level module
--> $DIR/recursive-static-definition.rs:1:1
|
Expand Down

0 comments on commit ca76eb8

Please sign in to comment.