Skip to content
Merged
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
169 changes: 168 additions & 1 deletion src/v1/stage0/src/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,128 @@ mod compiler_tests {
result.expect("shell_service_exit_error_arm_is_boxed panicked");
}

// REGRESSION CONTROL for a property that holds BY CONSTRUCTION today, and which nothing
// else would notice losing. DESIGN section 4b(4): the evidence stays enrolled.
//
// A function-VALUE realizes as `Rc<dyn Fn(..)>`; a function-typed PARAMETER realizes as an
// `impl Fn(..) + Clone` bound, and `Rc<F>` carries no blanket `impl Fn`, so passing the first
// into the second is an E0277 seam. rust_call_arg_function_value_adapt closes it by wrapping
// the argument in a forwarding closure, and it decides on
// `param_node_type_expr(n: param).params |> count > 0`.
//
// THAT IS THE SAME EXPRESSION ON THE SAME NODE that emit_param/emit_rust_param_type use to
// decide whether to emit the `impl Fn` bound at all. So the adapter fires exactly where the
// seam exists, and cannot fire where it does not -- the two predicates cannot disagree while
// they remain one expression. This control is what notices if they are ever forked.
//
// WHAT THIS ESTABLISHES, AT ITS DECLARED GRAIN. Emission SUCCEEDS on this fixture -- zero
// error diagnostics, asserted FIRST, so a refusal fails here rather than being matched past
// -- and the emitted bytes carry, for this fixture, all six of:
// the function-valued producer emitted as `Rc<dyn Fn(..)>`;
// the declared-arrow formal emitted as `impl Fn(..) + Clone`;
// a forwarding closure emitted AT that formal;
// the bare type-variable formal carrying NO Fn bound;
// NO forwarding closure at that formal;
// and hence the two emitter decisions staying aligned on this fixture.
// Those are structural facts about the emitter, and substring matching can decide them.
// That list is the WHOLE claim.
//
// IT DOES NOT ESTABLISH THAT THE E0277 SEAM IS CLOSED, AND NOTHING EXECUTING TODAY DOES.
// Whether these bytes compile is a RUSTC verdict, and no amount of exact rendering is
// evidence about one; a substring assertion could not decide it at any lane membership.
//
// NO ENROLLED FAIL-CLOSED RUSTC CONSUMER COVERS THIS SYNTHETIC FIXTURE. That is a statement
// about the whole tree as it stands, not a pointer at some other test that would do it --
// deliberately, because naming a candidate invites the next reader to read 'not yet' as
// 'once someone schedules it', and a candidate cited that way becomes coverage in the telling.
//
// NO PER-FIXTURE RUSTC CONSUMER IS BUILT HERE EITHER. probe.rs references the crate runtime
// (im::vector, v1_std_core, the artifact types) and does not stand alone, and a second
// per-fixture compile path is the parallel authority section 6 warns about. The standing
// execution half would be a real COMMITTED occurrence of this adapter shape joined BY
// IDENTITY to a rustc consumer that runs and REFUSES. That join does not exist today.
//
// WHY THE BARE TYPE VARIABLE IS THE DISCRIMINATING HALF. It was reported (by me, wrongly) as a
// silent defect: a parameter declared `T` has arity 0, so no adapter is emitted, and nothing
// refuses. The bytes below are the refutation -- `T` renders as a plain generic with NO Fn
// bound, so there is no seam to close, and adapting there would be a fabricated repair.
// Arity is invariant under substitution anyway: instantiation changes an arrow's type
// ARGUMENTS, never its parameter count.
#[test]
fn function_value_adapter_fires_exactly_where_the_impl_fn_bound_is_emitted() {
let result = std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let source = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {
path: "probe.dag".to_string(),
content: "module probe\nfn add_one(x: Int) -> Int { x + 1 }\nfn make_adder() -> fn(Int) -> Int { fn(n) { add_one(x: n) } }\nfn hold_tv<T>(value: T) -> T { value }\nfn apply_arrow(f: fn(Int) -> Int, v: Int) -> Int { f(v) }\nfn through_type_variable() -> Int { apply_arrow(f: hold_tv(value: make_adder()), v: 1) }\nfn through_declared_arrow() -> Int { apply_arrow(f: make_adder(), v: 1) }\n".to_string(),
});
let r = crate::v1_compiler_compile::compile_sources(
std::rc::Rc::new(im::vector![source]),
crate::v1_compiler_artifact::RenderTarget::Rust,
);
let errors: Vec<_> = r
.diagnostics
.iter()
.filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))
.collect();
assert!(
errors.is_empty(),
"the fixture is ordinary admissible source; a refusal here is a compiler \
defect rather than a defect in the fixture. Got: {:?}",
errors
);
let emitted = r
.files
.iter()
.find(|f| f.path == "src/probe.rs")
.map(|f| f.content.clone())
.expect("probe module must emit src/probe.rs");

// (1) the producer side of the seam: a function VALUE is the Rc carrier.
assert!(
emitted.contains("pub fn make_adder() -> Rc<dyn Fn(i64) -> i64>"),
"a function-valued return must realize as the Rc carrier, got:\n{}",
emitted
);
// (2) a DECLARED-ARROW parameter carries the impl Fn bound -- the seam exists here.
assert!(
emitted.contains("pub fn apply_arrow(f: impl Fn(i64) -> i64 + Clone, v: i64)"),
"a declared-arrow parameter must carry the impl Fn bound, got:\n{}",
emitted
);
// (3) a BARE TYPE VARIABLE parameter carries NO Fn bound -- no seam exists here.
assert!(
emitted.contains("pub fn hold_tv<T: Clone>(value: T) -> T"),
"a bare type-variable parameter must render as a plain generic with no Fn \
bound -- if this ever gains one, the adapter predicate must move with it, \
got:\n{}",
emitted
);
// (4) THE ALIGNMENT, both directions. The adapter wraps the argument at the
// impl-Fn-bound parameter, and does NOT wrap it at the type-variable parameter --
// whose argument is equally a call result, so shape alone does not explain it.
assert!(
emitted.contains(
"apply_arrow({ let __adapt_f = hold_tv(make_adder()); move |__adapt_a0| __adapt_f(__adapt_a0) }, 1)"
),
"the forwarding adapter must be emitted at the impl Fn parameter, got:\n{}",
emitted
);
assert!(
!emitted.contains("hold_tv({ let __adapt_f"),
"no adapter may be injected at a parameter that carries no Fn bound -- that \
would be a fabricated repair of a seam that does not exist, got:\n{}",
emitted
);
})
.expect("failed to spawn thread")
.join();
result.expect(
"function_value_adapter_fires_exactly_where_the_impl_fn_bound_is_emitted panicked",
);
}

#[test]
fn method_existence_wall_witness() {
// DISCRIMINATING RED for method_existence_wall_note. Before the wall an
Expand Down Expand Up @@ -2568,7 +2690,9 @@ mod compiler_tests {
let shared = std::rc::Rc::new(im::OrdSet::new());
let generics = std::rc::Rc::new(im::Vector::new());
let variant_to_enum = std::rc::Rc::new(HashMap::new());
let env = crate::v1_compiler_infer_env::empty_type_env();
let mut env_value = (*crate::v1_compiler_infer_env::empty_type_env()).clone();
env_value.unit_variant_index_observed = true;
let env = std::rc::Rc::new(env_value);
let arg = named_type_node("Int");
let applied = shaped_type_node("std.algebra.FreeMonoid", vec![arg]);
let rendered = crate::v1_compiler_emit_rust::render_rust_applied_type(
Expand All @@ -2584,6 +2708,49 @@ mod compiler_tests {
"applied-type base must not emit namespace dots in generic position"
);
assert_eq!(rendered, "Vec<i64>");
let marker = named_type_node("Time");
let contribution =
std::rc::Rc::new(crate::v1_compiler_infer_env::UnitVariantContribution {
count: 1,
variant: marker,
});
let by_parent = crate::v1_rt::rc_map_insert(
crate::v1_rt::rc_empty_map(),
"Quantity".to_string(),
contribution,
);
let mut populated_env_value = (*crate::v1_compiler_infer_env::empty_type_env()).clone();
populated_env_value.unit_variant_index = crate::v1_rt::rc_map_insert(
crate::v1_rt::rc_empty_map(),
"Time".to_string(),
by_parent,
);
populated_env_value.unit_variant_index_observed = true;
let populated = crate::v1_compiler_emit_rust::render_rust_applied_type(
shaped_type_node("Box", vec![named_type_node("Time")]),
std::rc::Rc::new(im::Vector::new()),
std::rc::Rc::new(im::OrdSet::new()),
std::rc::Rc::new(HashMap::new()),
std::rc::Rc::new(HashMap::new()),
std::rc::Rc::new(populated_env_value),
);
assert_eq!(
populated, "Box<Time>",
"a populated unit-variant census must preserve the selected marker identity"
);
let unavailable = crate::v1_compiler_emit_rust::render_rust_applied_type(
shaped_type_node("Box", vec![named_type_node("Time")]),
std::rc::Rc::new(im::Vector::new()),
std::rc::Rc::new(im::OrdSet::new()),
std::rc::Rc::new(HashMap::new()),
std::rc::Rc::new(HashMap::new()),
crate::v1_compiler_infer_env::empty_type_env(),
);
assert!(
unavailable.contains("unit-variant marker identity evidence unavailable for Time"),
"an unobserved empty census must refuse instead of answering non-marker: {}",
unavailable
);
}

fn optional_typed_arg_node() -> std::rc::Rc<crate::v1_std_core::Node> {
Expand Down
2 changes: 1 addition & 1 deletion src/v1/stage0/src/std_realization_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ pub struct WalkPlan<F: Clone> {
pub struct WalkPopulationBudgetRefusal {
pub population: String,
pub plan_site: String,
pub population_index: Nat,
pub population_index: i64,
pub active_unit: String,
pub elapsed: Millisecond,
pub budget: Millisecond,
Expand Down
Loading