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
625 changes: 603 additions & 22 deletions crates/jett_codegen_cranelift/src/emit.rs

Large diffs are not rendered by default.

115 changes: 114 additions & 1 deletion crates/jett_codegen_cranelift/src/emit/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,113 @@ impl Translator<'_, '_> {
self.builder.ins().stack_store(zero, slot, 0);
Ok(())
}
pub(super) fn call_generation_storage(
&self,
generation: jett_mir::CallOwnerGenerationId,
root: Option<jett_mir::LocalId>,
span: Span,
) -> Result<NativeCallGenerationStorage, CodegenError> {
let mut matches = self
.generation_slots
.iter()
.filter(|storage| storage.generation == generation);
let storage = matches.next().copied().ok_or_else(|| {
contract_error(
self.symbol,
span,
"call owner generation has no validated native storage",
)
})?;
if matches.next().is_some() || root.is_some_and(|root| root != storage.root) {
return Err(contract_error(
self.symbol,
span,
"call owner generation changes its exact root or storage",
));
}
Ok(storage)
}
pub(super) fn replace_call_generation(
&mut self,
generation: jett_mir::CallOwnerGenerationId,
root: jett_mir::LocalId,
rhs_owner: jett_mir::LocalId,
span: Span,
) -> Result<(), CodegenError> {
let storage = self.call_generation_storage(generation, Some(root), span)?;
let rhs = self
.local_types
.get(rhs_owner.index() as usize)
.ok_or_else(|| {
contract_error(
self.symbol,
span,
"call owner generation RHS local is absent",
)
})?;
let rhs_slot = self
.local_slots
.get(rhs_owner.index() as usize)
.copied()
.flatten()
.ok_or_else(|| {
contract_error(
self.symbol,
span,
"call owner generation RHS has no owning storage",
)
})?;
if rhs.id != rhs_owner
|| rhs.ty != storage.ty
|| rhs.view_source.is_some()
|| rhs_owner == root
|| rhs_slot == storage.root_slot
{
return Err(contract_error(
self.symbol,
span,
"call owner generation RHS is not its independent exact owner",
));
}
// RHS acquisition already completed at its unique checked owning Let.
// Its slot is transferred exactly once; no source expression runs here.
let next = self.builder.ins().stack_load(ir::types::I64, rhs_slot, 0);
self.clear_slot(rhs_slot);
let retired = self
.builder
.ins()
.stack_load(ir::types::I64, storage.retired_slot, 0);
let has_retired = self.builder.ins().icmp_imm(IntCC::NotEqual, retired, 0);
let attached = self.builder.create_block();
let replaced = self.builder.create_block();
let installed = self.builder.create_block();
self.builder
.ins()
.brif(has_retired, replaced, &[], attached, &[]);

self.builder.switch_to_block(attached);
let captured = self
.builder
.ins()
.stack_load(ir::types::I64, storage.root_slot, 0);
self.clear_slot(storage.root_slot);
self.builder
.ins()
.stack_store(captured, storage.owner_slot, 0);
let one = self.builder.ins().iconst(ir::types::I64, 1);
self.builder.ins().stack_store(one, storage.retired_slot, 0);
self.builder.ins().jump(installed, &[]);

self.builder.switch_to_block(replaced);
// The old captured owner stays in escrow. Only the independent current
// generation is dropped before its ordinary replacement is installed.
self.drop_slot(storage.root_slot)?;
self.builder.ins().jump(installed, &[]);

self.builder.switch_to_block(installed);
self.builder.ins().stack_store(next, storage.root_slot, 0);
Ok(())
}
pub(super) fn drop_temporaries(&mut self) -> Result<(), CodegenError> {
for &slot in &self.temporary_slots[..self.next_temporary] {
self.drop_slot(slot)?;
Expand All @@ -1384,7 +1491,13 @@ impl Translator<'_, '_> {
}
pub(super) fn drop_all(&mut self) -> Result<(), CodegenError> {
self.drop_temporaries()?;
self.drop_dead_locals(&BTreeSet::new())
self.drop_dead_locals(&BTreeSet::new())?;
for index in 0..self.generation_slots.len() {
let storage = self.generation_slots[index];
self.drop_slot(storage.owner_slot)?;
self.clear_slot(storage.retired_slot);
}
Ok(())
}
pub(super) fn static_data(&mut self, bytes: &[u8]) -> Result<(Value, Value), CodegenError> {
self.static_data_with_functions(bytes, &[])
Expand Down
54 changes: 47 additions & 7 deletions crates/jett_codegen_cranelift/src/reachability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,12 @@ fn collect_function_references(function: &Function, references: &mut References<
collect_expression_references(condition, references);
}
}
StatementKind::EndCallView { .. }
// Generation operations use current Local operands only;
// their immutable Source archives are not callable roots.
StatementKind::OpenCallOwnerGeneration { .. }
| StatementKind::ReplaceCallOwnerGeneration { .. }
| StatementKind::CloseCallOwnerGeneration { .. }
| StatementKind::EndCallView { .. }
| StatementKind::ReflectedContainerReady { .. }
| StatementKind::IterationBorrow { .. }
| StatementKind::SequenceLength { .. }
Expand Down Expand Up @@ -351,6 +356,7 @@ fn collect_expression_references(expression: &Expression, references: &mut Refer
field_validation: _,
args,
evaluation_order: _,
..
} => {
for argument in args {
collect_expression_references(argument, references);
Expand Down Expand Up @@ -651,7 +657,7 @@ function root() returns int64:

#[test]
fn retained_calls_and_references_still_reach_uninhabited_specializations() {
for source in [
for (case, source) in [
r#"namespace app
function identity(value: int64) returns int64:
return value
Expand All @@ -664,7 +670,10 @@ function identity(value: int64) returns int64:
function root() returns function(int64) returns int64:
return identity
"#,
] {
]
.into_iter()
.enumerate()
{
let (mut program, types) = lower_source_with_types(source);
let callee = program
.functions
Expand All @@ -678,11 +687,42 @@ function root() returns function(int64) returns int64:
callee.locals[parameter].debug_ty = jett_types::TypeInterner::NEVER;
assert!(uninhabited_specialization(callee));
assert_eq!(reachable_names(&program), ["identity", "root"]);
// A fabricated surviving use must fail ordinary verification;
// suppressing implicit roots must never supply a `never` carrier.
// Reachability keeps both identities, but an earlier source
// packet rejection is independent of the native callable gate.
let result = crate::emit_host_object(&program, &types);
if case == 0 {
assert!(matches!(result, Err(CodegenError::InvalidMir(errors))
if errors.len() == 1 && errors[0].message
== "source function ownership signature differs from the exact HIR declaration"));
} else {
// Preserve the original reference-only refusal until its
// actual validation path is characterized separately.
assert!(matches!(
result,
Err(CodegenError::InvalidMirContract { .. })
));
}
let root = program
.functions
.iter()
.find(|function| function.identity.declaration.name == "root")
.expect("original checked root");
let expression = root
.blocks
.iter()
.find_map(|block| match &block.terminator.kind {
TerminatorKind::Return(Some(value)) => Some(value),
_ => None,
})
.expect("retained original call or reference");
let expected = if case == 0 {
"callable direct edge requires an uninhabited parameter"
} else {
"function value signature does not match target"
};
assert!(matches!(
crate::emit_host_object(&program, &types),
Err(CodegenError::InvalidMirContract { .. })
crate::verify::verify_callable_expression_for_test(&program, &types, root, expression),
Err(CodegenError::InvalidMirContract { message, .. }) if message == expected
));
}
}
Expand Down
77 changes: 75 additions & 2 deletions crates/jett_codegen_cranelift/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ pub(crate) fn verify_descriptor_bodies(
program: &Program,
types: &TypeInterner,
) -> Result<(), CodegenError> {
jett_mir::validate(program).map_err(CodegenError::InvalidMir)?;
jett_mir::validate_call_ownership(program, types).map_err(CodegenError::InvalidMir)?;
// Shared inline tables and impossible sum arms contain valid Never metadata
// that need not allocate a native frame slot. Validate original type shapes
// without demanding a carrier for an absent function result; every surviving
Expand Down Expand Up @@ -302,11 +302,35 @@ impl VerifiedProgram {
}
}

/// Unit-only access to the existing native expression integrity gate.
/// Outer emission independently checks the original caller ownership packet;
/// this helper supplies no preparation, callable frame or emission authority.
#[cfg(test)]
pub(crate) fn verify_callable_expression_for_test(
program: &Program,
types: &TypeInterner,
function: &Function,
expression: &Expression,
) -> Result<(), CodegenError> {
let verified = VerifiedProgram {
functions: Vec::new(),
by_mir_index: vec![None; program.functions.len()],
};
let verifier = Verifier {
program,
types,
verified: &verified,
phase: VerifierPhase::CallableNative,
metadata_functions: RefCell::new(HashSet::new()),
};
verifier.expression(function, expression)
}

pub(crate) fn verify_program(
program: &Program,
types: &TypeInterner,
) -> Result<VerifiedProgram, CodegenError> {
jett_mir::validate(program).map_err(CodegenError::InvalidMir)?;
jett_mir::validate_call_ownership(program, types).map_err(CodegenError::InvalidMir)?;

// Reachability may omit lifted bodies, but malformed source metadata must
// not disappear with an unused descriptor.
Expand Down Expand Up @@ -1020,6 +1044,55 @@ impl Verifier<'_> {
})?;
Ok(())
}
StatementKind::OpenCallOwnerGeneration { root, .. }
| StatementKind::ReplaceCallOwnerGeneration { root, .. } => {
let owner = function.local(*root).ok_or_else(|| {
self.contract_error(
function,
statement.span,
"call owner generation root is absent from the local table",
)
})?;
if owner.ty.index() as usize >= self.types.len()
|| !matches!(self.types.resolve(owner.ty), Type::TypeConstruction)
|| !owner.mutable
|| function.is_view_local(*root)
|| function.parameter_for_local(*root).is_some()
{
return Err(self.contract_error(
function,
statement.span,
"call owner generation root is not its exact mutable builder owner",
));
}
self.value_kind(function, owner.ty, "call owner generation root")?;
if let StatementKind::ReplaceCallOwnerGeneration { rhs_owner, .. } = &statement.kind
{
let rhs = function.local(*rhs_owner).ok_or_else(|| {
self.contract_error(
function,
statement.span,
"call owner generation RHS is absent from the local table",
)
})?;
if rhs_owner == root
|| rhs.ty != owner.ty
|| function.is_view_local(*rhs_owner)
|| function.parameter_for_local(*rhs_owner).is_some()
{
return Err(self.contract_error(
function,
statement.span,
"call owner generation RHS is not its independent owning builder",
));
}
self.value_kind(function, rhs.ty, "call owner generation RHS")?;
}
// Exact sites, private Source joins and Open/Replace/Close CFG
// authority were checked by the original all-body MIR gate.
Ok(())
}
StatementKind::CloseCallOwnerGeneration { .. } => Ok(()),
StatementKind::CheckRefinement { local, call, .. } => {
let local = function.local(*local).ok_or_else(|| {
self.contract_error(
Expand Down
Loading
Loading