Skip to content
Merged
52 changes: 52 additions & 0 deletions dag/gunbc/stage0_emitted_population_manifest.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module gunbc.stage0_emitted_population_manifest

import std.types { String }

// THE NAME OF THE ARTIFACT IN WHICH THE EMITTER DECLARES THE POPULATION IT PRODUCED.
//
// This module exists to hold ONE string, and that is the point rather than an accident of
// factoring. The manifest has two ends -- `v1.compiler.emit_rust` writes it and
// `gunbc.stage0_rust_host_observation` reads it -- and if each spelled the basename itself,
// the artifact would have two names for one file, which is the nicknaming failure DESIGN 3
// names and the exact class the manifest exists to end. A shared leaf module is the smallest
// carrier that lets both ends resolve the same symbol without the reader importing the whole
// emitter's closure to get at one literal.
//
// ONLY THE BASENAME IS SHARED, DELIBERATELY. The two ends compose different roots because they
// address different spaces: the emitter writes target-relative (under `rust_source_root()`,
// i.e. `src/`), the observation addresses repository-relative (under `stage0_package_root`,
// i.e. `src/v1/stage0/`). Hoisting a single "path" here would force one of them to hold a path
// in a space it does not speak, so the shared fact is the part that is genuinely one fact.
data emitted_population_manifest_basename: String = "emitted_population.rs"

// The manifest's line grammar, stated once for the same reason the basename is.
//
// The emitter renders one line per path as `line_prefix ++ path`, joined by the separator; the
// observation recovers the population by splitting on the separator and stripping the prefix.
// One grammar read in both directions (DESIGN 4) -- the structural inverse, not a second parser.
//
// WHY THE ARTIFACT IS A `.rs` OF COMMENT LINES, WHICH LOOKS ODD UNTIL YOU SEE WHAT IT BUYS.
// Two demands pull opposite ways. GATING wants the manifest inside the population
// `required_regen` already compares, and that population is `.rs`-shaped end to end: every
// compared member is rustfmt-normalized before its digest is taken, so a `.txt` member is
// REFUSED at normalization ("expected one of `!` or `::`, found `.`") -- measured, not
// predicted. READING wants recovery without parsing Rust, because a text-parse standing in for
// a modeled fact is exactly what this lane spent the day deleting (gunbc#8690, cssl_assemble's
// seed lib.rs oracle), and a generated `const &[&str]` would put one right back.
//
// A file of nothing but `//` lines satisfies both. It is valid Rust, so it enters the compared
// population by construction and normalizes to itself (a comments-only file is already a
// rustfmt fixed point, which is the property DESIGN's two-consumer rule demands). And it is
// read by splitting and stripping a literal prefix -- no Rust grammar is involved on the
// reading side at all. It is deliberately NOT a module: it is appended to the emitted file list
// AFTER `lib.rs` is rendered, so no `pub mod` line names it and nothing compiles it.
// DISSOLUTION: this shape is owed to the compared population being Rust-shaped end to end, not
// to anything about the fact itself. When that population becomes medium-agnostic -- when
// `required_regen`'s comparison normalizes an artifact by its declared medium rather than by
// assuming Rust -- the manifest becomes the line-delimited text it wants to be, this paragraph
// deletes, and the `.rs` extension goes with it. Until then the extension is load-bearing and
// "cleaning it up" into a `const &[&str]` would silently reintroduce a Rust parse on the read
// side, which is the class gunbc#8690 deleted.
data emitted_population_manifest_line_prefix: String = "// "

data emitted_population_manifest_line_separator: String = "\n"
48 changes: 47 additions & 1 deletion src/v1/05_emit_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ import std.trait_derive_shape { phantom_opaque_carrier_derive_traits }
import extdeps.cargo_version { render_cargo_package_header_prefix }
import gunbc.stage0_crate_layout_generated { generated_pub_mod_block }
import gunbc.rust_decl_type_overlay { rust_decl_type_container_overlay_is_admitted }
import gunbc.stage0_emitted_population_manifest {
emitted_population_manifest_basename,
emitted_population_manifest_line_prefix,
emitted_population_manifest_line_separator
}
import v1.compiler.languages {
scaffold_for_target, serialization_for_target,
TestConventions, ItemKeywords,
Expand Down Expand Up @@ -2560,10 +2565,51 @@ fn emit_rust(typed: ResolvedGraph) -> EmitResult {
}
let all_mod_files = concat(module_files, v2_std_text_stub, v2_std_integer_stub, [rt_file], dry_run_file)
let lib_file = emit_lib_rs_from_files(all_module_files: all_mod_files, has_compiler_tests: has_pipeline)
let files = concat([cargo, lib_file, main_file], all_mod_files, compiler_tests_file, test_files)
let emitted_files = concat([cargo, lib_file, main_file], all_mod_files, compiler_tests_file, test_files)
let population_manifest = emit_emitted_population_manifest(files: emitted_files)
let files = concat(emitted_files, [population_manifest])
EmitResult { files: files, diagnostics: [] }
}

// THE EMITTER DECLARES THE POPULATION IT PRODUCED. This is the one place in the repository
// where that fact exists as a value: `files` below is the complete emitted population, typed,
// before anything reaches disk. It was then DISCARDED, and every downstream consumer rebuilt
// it from whatever residue it could see -- lib.rs, the "Generated by v1 compiler" banner, the
// complement of the crate-layout claim, a directory listing. Those reconstructions disagree
// exactly during drift, which is the root the manifest closes: one authority, N derivations.
//
// lib.rs is NOT this fact and never was. It is one PROJECTION of the same value -- the
// top-level pub-mod block of one crate -- which is why nine live stage0 modules are absent
// from it (reached by `mod` from a parent, by `#[path]` from another crate, or by a non-pub
// `mod`). The manifest is a second projection of that value, and a total one.
//
// DECLARED UNFILTERED, deliberately. Every path the emit produced, not "the generated ones".
// Consumers cut it to their own question (`required_regen_host` drops non-.rs and
// hand-maintained; the lifecycle scaffold wants a different cut). Filtering at the write would
// bake one consumer's question into the fact and guarantee the next consumer reconstructs --
// which is the failure this exists to end.
//
// The manifest LISTS ITSELF, because the emitter does produce it. That is a fixed point, not
// a circularity: the content is a function of the PATHS, and the manifest's own path is known
// before its content is rendered, so a second emit over the same input yields identical bytes.
fn emitted_population_manifest_path() -> String {
concat(rust_source_root(), emitted_population_manifest_basename)
}

fn emit_emitted_population_manifest(files: List<TextFile>) -> TextFile {
let declared = concat(files |> map(f => f.path), [emitted_population_manifest_path()])
let lines = declared
|> sort_by(path => path)
|> map(path => concat(emitted_population_manifest_line_prefix, path))
TextFile {
path: emitted_population_manifest_path(),
content: concat(
lines |> join(separator: emitted_population_manifest_line_separator),
emitted_population_manifest_line_separator
)
}
}

fn emit_v2_rt_module() -> TextFile {
TextFile { path: concat(rust_source_root(), "v1_rt", rust_source_ext()), content: rust_runtime_source() }
}
Expand Down
133 changes: 133 additions & 0 deletions src/v1/stage0/src/emitted_population.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Cargo.toml
// src/compiler_tests.rs
// src/emitted_population.rs
// src/extdeps_cargo.rs
// src/extdeps_cargo_version.rs
// src/extdeps_container_oci_digest.rs
// src/extdeps_external_authority.rs
// src/extdeps_languages_dag_emit.rs
// src/extdeps_languages_dag_syntax.rs
// src/extdeps_languages_dag_types.rs
// src/extdeps_languages_go_emit.rs
// src/extdeps_languages_go_syntax.rs
// src/extdeps_languages_go_types.rs
// src/extdeps_languages_python_emit.rs
// src/extdeps_languages_python_syntax.rs
// src/extdeps_languages_python_types.rs
// src/extdeps_languages_rust_derive_contracts.rs
// src/extdeps_languages_rust_emit.rs
// src/extdeps_languages_rust_syntax.rs
// src/extdeps_languages_rust_types.rs
// src/extdeps_units_dimensionless.rs
// src/extdeps_units_iec_80000_13.rs
// src/extdeps_units_iso8601.rs
// src/extdeps_units_iso_80000_3.rs
// src/extdeps_uri.rs
// src/extdeps_uri_path.rs
// src/extdeps_version.rs
// src/extdeps_version_semver.rs
// src/gunbc_rust_decl_type_overlay.rs
// src/gunbc_stage0_crate_layout_generated.rs
// src/gunbc_stage0_crate_partition_generated.rs
// src/gunbc_stage0_emitted_population_manifest.rs
// src/lib.rs
// src/main.rs
// src/std_algebra.rs
// src/std_checked_arithmetic.rs
// src/std_coercion.rs
// src/std_computation.rs
// src/std_constructors.rs
// src/std_content_hash.rs
// src/std_currency.rs
// src/std_decl_ref.rs
// src/std_disposition.rs
// src/std_dissolution.rs
// src/std_effects.rs
// src/std_emit_model.rs
// src/std_error_primitives.rs
// src/std_execution_mode.rs
// src/std_graph.rs
// src/std_http_path.rs
// src/std_induction.rs
// src/std_integer.rs
// src/std_interface_summary.rs
// src/std_keyed_roster.rs
// src/std_keyed_row.rs
// src/std_logic.rs
// src/std_machine_constraints.rs
// src/std_magnitude.rs
// src/std_measure.rs
// src/std_nat.rs
// src/std_node.rs
// src/std_occurrence_binding.rs
// src/std_occurrence_binding_candidates.rs
// src/std_occurrence_binding_resolve.rs
// src/std_occurrence_identity.rs
// src/std_pareto.rs
// src/std_process_termination.rs
// src/std_realization_schedule.rs
// src/std_reference_binding_observation.rs
// src/std_roster_frontier.rs
// src/std_serialization.rs
// src/std_source_annotation.rs
// src/std_syntax.rs
// src/std_termination.rs
// src/std_trait_derive_shape.rs
// src/std_types.rs
// src/std_unicode_types.rs
// src/std_witness_admission.rs
// src/v1_compiler_annotation_bind.rs
// src/v1_compiler_artifact.rs
// src/v1_compiler_closure_stub_v2_std_integer_rust.rs
// src/v1_compiler_closure_stub_v2_std_text_rust.rs
// src/v1_compiler_coercion.rs
// src/v1_compiler_compile.rs
// src/v1_compiler_compiler_tests_rust.rs
// src/v1_compiler_complexity.rs
// src/v1_compiler_dag_collect.rs
// src/v1_compiler_dag_collect_support.rs
// src/v1_compiler_effect_derivation.rs
// src/v1_compiler_emit.rs
// src/v1_compiler_emit_core_support.rs
// src/v1_compiler_emit_go.rs
// src/v1_compiler_emit_python.rs
// src/v1_compiler_emit_rust.rs
// src/v1_compiler_expected_red_roster_join.rs
// src/v1_compiler_frontend_observation.rs
// src/v1_compiler_infer.rs
// src/v1_compiler_infer_access.rs
// src/v1_compiler_infer_cycle.rs
// src/v1_compiler_infer_emit_info.rs
// src/v1_compiler_infer_env.rs
// src/v1_compiler_infer_items.rs
// src/v1_compiler_infer_lookup.rs
// src/v1_compiler_infer_method.rs
// src/v1_compiler_infer_occurrence_binding.rs
// src/v1_compiler_infer_patterns.rs
// src/v1_compiler_infer_resolve.rs
// src/v1_compiler_infer_service.rs
// src/v1_compiler_infer_sigs.rs
// src/v1_compiler_infer_types.rs
// src/v1_compiler_languages.rs
// src/v1_compiler_normalize.rs
// src/v1_compiler_ownership.rs
// src/v1_compiler_parse.rs
// src/v1_compiler_resolve.rs
// src/v1_compiler_runtime_go.rs
// src/v1_compiler_runtime_rust.rs
// src/v1_compiler_stage0_crates.rs
// src/v1_compiler_tokenize.rs
// src/v1_compiler_trace.rs
// src/v1_compiler_trait_bound_witness.rs
// src/v1_compiler_trait_derive_emit.rs
// src/v1_compiler_workspace_members.rs
// src/v1_gunbc_namespace_reference_derived_closure_production_observations.rs
// src/v1_gunbc_occurrence_binding_parser_walk.rs
// src/v1_probe_emit_interp.rs
// src/v1_rt.rs
// src/v1_std_core.rs
// src/v1_test_non_ascii_perf_fixture.rs
// src/v1_tests_claim_checkpoint_identity_keying_witness_test.rs
// src/wt_a.rs
// src/wt_b.rs
// src/wt_common.rs
36 changes: 36 additions & 0 deletions src/v1/stage0/src/gunbc_stage0_emitted_population_manifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Generated by v1 compiler -- do not edit.
// Source module: gunbc.stage0_emitted_population_manifest

use crate::v1_rt;
use crate::v1_rt::{VecCompat, VecJoin};
use crate::NonEmptyBTreeSet;
use crate::NonEmptyVec;
use im::{vector as vec, HashMap, OrdSet as BTreeSet, Vector as Vec};
use std::rc::Rc;

pub fn emitted_population_manifest_basename() -> String {
thread_local! {
static CACHED: String = {
"emitted_population.rs".to_string()
};
}
CACHED.with(|c: &String| c.clone())
}

pub fn emitted_population_manifest_line_prefix() -> String {
thread_local! {
static CACHED: String = {
"// ".to_string()
};
}
CACHED.with(|c: &String| c.clone())
}

pub fn emitted_population_manifest_line_separator() -> String {
thread_local! {
static CACHED: String = {
"\n".to_string()
};
}
CACHED.with(|c: &String| c.clone())
}
1 change: 1 addition & 0 deletions src/v1/stage0/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub mod extdeps_version_semver;
pub mod gunbc_rust_decl_type_overlay;
pub mod gunbc_stage0_crate_layout_generated;
pub mod gunbc_stage0_crate_partition_generated;
pub mod gunbc_stage0_emitted_population_manifest;
pub mod std_algebra;
pub mod std_checked_arithmetic;
pub mod std_coercion;
Expand Down
31 changes: 29 additions & 2 deletions src/v1/stage0/src/required_regen_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,10 +710,35 @@ fn compile_stage0(workspace: &Path) -> Result<HashMap<String, String>, String> {
Ok(out)
}

// ONE AUTHORITY FOR "WHAT THE REGEN COMPARES", read from both sides.
//
// `generated_basenames_from_emit` and `committed_generated_basenames` answer the same question
// about two different populations -- the emit and the committed tree -- and each carried its own
// copy of the membership rule. That is the fork this whole lane keeps finding: two readers of one
// fact, which agree until they do not, and here they sat inside the very file whose job is to
// detect that class. They now share this predicate, so a file cannot be compared on one side and
// skipped on the other.
//
// THE EMITTED-POPULATION MANIFEST NEEDS NO EXCEPTION HERE, AND THAT IS BY CONSTRUCTION RATHER
// THAN BY LUCK. `emitted_population.rs` (v1.compiler.emit_rust, `emit_emitted_population_manifest`)
// is the emitter's declaration of what it produced, and it is emitted as a `.rs` of comment lines
// precisely so that this rule already admits it: it enters the compared population, the drift
// comparison, the tree digest and candidate verification with nothing named and nothing added.
// An earlier revision made it a `.txt` and named it in this predicate as an exception. That was
// refused by execution, not by review -- every compared member is rustfmt-normalized before its
// digest is taken, so the `.txt` failed to parse as Rust ("expected one of `!` or `::`, found
// `.`"). The refusal was the right one: the compared population is Rust-shaped end to end, and an
// artifact that wants its gating has to be Rust.
fn is_compared_generated_basename(basename: &str) -> bool {
basename.ends_with(".rs")
}

fn generated_basenames_from_emit(emitted: &HashMap<String, String>) -> Vec<String> {
let mut names: BTreeSet<String> = BTreeSet::new();
for path in emitted.keys() {
if path.ends_with(".rs") && !is_hand_maintained_path(path) {
if is_compared_generated_basename(emit_path_basename(path))
&& !is_hand_maintained_path(path)
{
// Basename, not the emit key: `committed_generated_basenames` keys on
// `file_name()`, and emit keys carry a `src/` prefix. Comparing the two
// key spaces made every file mismatch in both directions.
Expand Down Expand Up @@ -753,7 +778,9 @@ fn committed_generated_basenames(stage0_src: &Path) -> Result<Vec<String>, Strin
continue;
}
let basename = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if basename.ends_with(".rs") && !HAND_MAINTAINED_STAGE0_FILES.contains(&basename) {
if is_compared_generated_basename(basename)
&& !HAND_MAINTAINED_STAGE0_FILES.contains(&basename)
{
names.insert(basename.to_string());
}
}
Expand Down
Loading