Skip to content
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
49 changes: 45 additions & 4 deletions src/v1/05_emit_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -11216,6 +11216,25 @@ fn call_target_is_runtime_primitive(target: CallTargetIdentity) -> Bool {
// question the call site answers positionally today -- at which point a body walk can consume it and
// the whole-function refusal becomes decidable.
//
// THE PREDICATE IS ONLY TRUE AT THE RESIDUE POSITION, AND THAT IS A FACT ABOUT WHERE IT MAY BE
// ASKED RATHER THAN ABOUT WHAT IT COMPUTES. It answers "runtime primitive, no bridge, no projected
// declaration" -- which is the right question ONLY once every earlier arm has declined, because the
// emitter renders a family of primitives through DEDICATED ARMS that carry no bridge row: `with`
// (record update), `concat` past two arguments, the collection folds, the pipelines. Asked before
// those arms, it calls every one of them unrealized.
//
// MEASURED TWICE, both times by the seed failing on itself rather than by reading the dispatch. A
// body-grain consumer refused `v1.compiler.tokenize source_code_point` and `v1.compiler.infer
// typecheck_module`, and the rebuilt seed panicked on its own first tokenize. Then a call-expression
// consumer placed one level too early intercepted `with` in `v1.compiler.infer` -- the emitted
// `let typed_module = with(typed_base, ..)` took the seam refusal's type instead of the record
// update's, and the SECOND GENERATION failed to compile with `expected Rc<Node>, found
// Rc<HashMap<_, _>>`. Stage one built clean both times; only the fixed-point check caught it.
//
// So the only admissible consumer is the arm in `emit_typed_call` that already sits after `is_rt`
// and after the `call_target` match. A caller that wants this answer earlier does not want this
// question.
//
// THE BRIDGE TEST IS PART OF THE PREDICATE, NOT A GUARD ITS CALLERS ADD, and that placement was
// bought the hard way. A runtime primitive WITH a `v1_rt` row is realized -- the bridge IS its
// realization -- so "runtime primitive with no projected declaration" is not the question; it
Expand All @@ -11225,6 +11244,15 @@ fn call_target_is_runtime_primitive(target: CallTargetIdentity) -> Bool {
// `v1.compiler.tokenize source_code_point` -- an ordinary bridged function -- and the rebuilt binary
// panicked on its own first tokenize. A predicate that is only correct when every caller remembers
// to pre-filter it is a wall with a hole per consumer, so the filter moved inside.
// The seam's own name, for the refusal text. Empty when the target is not a runtime primitive at
// all, which the predicate beside this one has already excluded at every call site that asks.
fn call_target_unrealized_primitive_name(target: CallTargetIdentity) -> String {
match call_target_runtime_primitive_name(target: target) {
Present { value: primitive_name } => primitive_name
Absent => ""
}
}

fn call_target_is_unrealized_primitive(target: CallTargetIdentity) -> Bool {
match call_target_runtime_primitive_name(target: target) {
Absent => false
Expand Down Expand Up @@ -11641,7 +11669,7 @@ fn emit_typed_call_expr(func: String, args: List<Node>, inferred: InferredNode?,
_ => "v1_rt::rc_empty_set::<_>()"
}
} else {
emit_typed_call(func: func, args: args, call_semantics: call_semantics, callee_is_function_value: callee_is_function_value, call_target: call_target, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info)
emit_typed_call(func: func, args: args, inferred: inferred, call_semantics: call_semantics, callee_is_function_value: callee_is_function_value, call_target: call_target, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info)
}

let wraps_in_rc = match call_target_runtime_primitive_name(target: call_target) {
Expand Down Expand Up @@ -11818,7 +11846,7 @@ fn lambda_argument_scope(arg: Node, scope: InferScope) -> InferScope {
// because a BARE leaf can name two modules, which is precisely the state a resolved owner rules
// out. An undetermined target has no identifier to emit and gets a located refusal rather than
// a fall back to the authored spelling, which is the re-lookup this carrier exists to remove.
fn emit_typed_call(func: String, args: List<Node>, call_semantics: CallSemantics?, callee_is_function_value: Bool, call_target: CallTargetIdentity, registry: Map<String, ItemInfo>, scope: InferScope, depth: Int, shared_types: Set<String>, emit_info: EmitGraphInfo) -> String {
fn emit_typed_call(func: String, args: List<Node>, inferred: InferredNode?, call_semantics: CallSemantics?, callee_is_function_value: Bool, call_target: CallTargetIdentity, registry: Map<String, ItemInfo>, scope: InferScope, depth: Int, shared_types: Set<String>, emit_info: EmitGraphInfo) -> String {
if callee_is_function_value {
return emit_typed_function_value_call(func: func, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info)
}
Expand Down Expand Up @@ -11972,7 +12000,20 @@ fn emit_typed_call(func: String, args: List<Node>, call_semantics: CallSemantics
} }
let func_name = if callee_self_capture { concat(func_ident, ".clone()") } else { func_ident }
let call_str = if unrealized_primitive_call {
func_ident
match inferred {
Present { value: Resolved { node: ret_type } } =>
concat(
"v1_rt::unrealized_host_seam::<",
render_rust_type(n: ret_type, shared_types: shared_types, source_indices: si, emit_info: emit_info),
">(\"",
escape_string_literal_body(s: concat(
concat("primitive ", call_target_unrealized_primitive_name(target: call_target)),
" has no v1_rt realization and no declaration to emit: give it a row in extdeps.languages.rust.emit rt_function_registry and a body in v1.runtime_rust, or delete its consumers"
)),
"\")"
)
_ => func_ident
}
} else {
if is_rt && func == "concat" && all_args |> count > 2 {
emit_nested_rt_concat(remaining: all_args, acc: "", shared_types: shared_types)
Expand Down Expand Up @@ -14430,7 +14471,7 @@ fn emit_rust_tco_non_self_call(frame: TcoFrame, registry: Map<String, ItemInfo>,
match frame.expr.expr_data {
ExprCall { call_semantics: cs, descent_evidence: _ } =>
let f = expr_call_func_at(texpr: frame.expr, source_indices: frame.scope.type_env.source_indices)
let call_str = emit_typed_call(func: f, args: frame.expr.children, call_semantics: cs, callee_is_function_value: call_semantics_is_function_value(cs: cs), call_target: call_semantics_target(cs: cs), registry: registry, scope: frame.scope, depth: frame.depth, shared_types: shared_types, emit_info: emit_info)
let call_str = emit_typed_call(func: f, args: frame.expr.children, inferred: frame.expr.inferred, call_semantics: cs, callee_is_function_value: call_semantics_is_function_value(cs: cs), call_target: call_semantics_target(cs: cs), registry: registry, scope: frame.scope, depth: frame.depth, shared_types: shared_types, emit_info: emit_info)
concat("break ", call_str, ";")
_ => emit_error_expr(message: "emit_rust_tco_non_self_call expected ExprCall", target: Rust)
}
Expand Down
26 changes: 26 additions & 0 deletions src/v1/runtime_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -589,13 +589,39 @@ fn rt_rc_container_ops() -> String {
// runtime is mirrored into the partition's foundation crate, so `v1_rt::` resolves from a
// partitioned crate as well as from the monolithic one. A crate-root definition would not: the
// partition crates re-export only what stage0_crates names explicitly.
// THE DIVERGING SEAM HELPER, AND WHY IT IS GENERIC RATHER THAN A BARE `panic!`.
//
// An unrealized host seam must refuse, and `panic!` is the refusal: a primitive whose body is a bare
// self-call has no behaviour on this target, and emitting that shape verbatim produces a function
// that compiles, runs and returns to no caller (DESIGN section 5 silent wrongness). What `panic!`
// alone cannot do is stand where its RESULT IS CONSUMED. `!` coerces to any type in value position,
// but it is not a method receiver, so `panic!("...").len()` refuses with E0599 and every argument
// after it with `unreachable`. Measured on the v2-native CLI closure: two seams, five errors, none
// of which named a seam.
//
// `unrealized_host_seam::<T>` is the same divergence with a NAME FOR ITS TYPE. The emitter renders
// the turbofish from the call's own inferred type, so the expression is concrete, method resolution
// works, and the program still cannot get past it. Nothing is fabricated: the function has no return
// path.
//
// WHY NOT REFUSE THE WHOLE FUNCTION INSTEAD, which type-checks trivially because `!` coerces in
// return position. That was built and withdrawn: deciding "does this body reach an unrealized seam"
// needs a realizability predicate the emitter does not have -- `rt_functions()` is not the set of
// realizable primitives, since collection folds, pipelines and the operators are rendered by
// dedicated arms with no bridge row -- so a body walk refused `v1.compiler.tokenize
// source_code_point` and `v1.compiler.infer typecheck_module`, and the rebuilt seed panicked on its
// own first tokenize. Typing the expression needs no such predicate: the call site already knows
// what the call was supposed to return.
fn rt_filesystem() -> String {
concat(
rust_file_create_new_canonical_block(), "\n",
"#[derive(Debug, Clone)]\n",
"pub struct FilesystemReadResult {\n",
" pub content: String,\n",
"}\n\n",
"pub fn unrealized_host_seam<T>(seam: &str) -> T {\n",
" panic!(\"{}\", seam);\n",
"}\n\n",
"pub fn filesystem_read(path: String) -> FilesystemReadResult {\n",
" let content = std::fs::read_to_string(&path)\n",
" .unwrap_or_else(|e| panic!(\"failed to read {}: {}\", path, e));\n",
Expand Down
15 changes: 14 additions & 1 deletion src/v1/stage0/src/v1_compiler_emit_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23828,6 +23828,13 @@ pub fn call_target_is_runtime_primitive(target: Rc<CallTargetIdentity>) -> bool
(call_target_runtime_primitive_name(target.clone()) != std::option::Option::None)
}

pub fn call_target_unrealized_primitive_name(target: Rc<CallTargetIdentity>) -> String {
match call_target_runtime_primitive_name(target.clone()) {
Some(primitive_name) => primitive_name.clone(),
std::option::Option::None => "".to_string(),
}
}

pub fn call_target_is_unrealized_primitive(target: Rc<CallTargetIdentity>) -> bool {
match call_target_runtime_primitive_name(target.clone()) {
std::option::Option::None => false,
Expand Down Expand Up @@ -24908,6 +24915,7 @@ pub fn emit_typed_call_expr(
emit_typed_call(
func.clone(),
args.clone(),
inferred.clone(),
call_semantics.clone(),
callee_is_function_value.clone(),
call_target.clone(),
Expand Down Expand Up @@ -25142,6 +25150,7 @@ pub fn lambda_argument_scope(arg: Rc<Node>, scope: Rc<InferScope>) -> Rc<InferSc
pub fn emit_typed_call(
func: String,
args: Rc<Vec<Rc<Node>>>,
inferred: Option<Rc<InferredNode>>,
call_semantics: Option<Rc<CallSemantics>>,
callee_is_function_value: bool,
call_target: Rc<CallTargetIdentity>,
Expand Down Expand Up @@ -25666,7 +25675,10 @@ pub fn emit_typed_call(
func_ident.clone()
};
let call_str = if unrealized_primitive_call.clone() {
func_ident.clone()
match inferred.clone().as_deref().cloned() {
Some(InferredNode::Resolved { node: ret_type, .. }) => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("v1_rt::unrealized_host_seam::<".to_string(), render_rust_type(ret_type.clone(), shared_types.clone(), si.clone(), emit_info.clone())), ">(\"".to_string()), crate::v1_compiler_emit_core_support::escape_string_literal_body(v1_rt::concat(v1_rt::concat("primitive ".to_string(), call_target_unrealized_primitive_name(call_target.clone())), " has no v1_rt realization and no declaration to emit: give it a row in extdeps.languages.rust.emit rt_function_registry and a body in v1.runtime_rust, or delete its consumers".to_string()))), "\")".to_string()),
_ => func_ident.clone(),
}
} else {
if ((is_rt.clone() && (func.clone() == "concat".to_string()))
&& ((all_args.clone().len() as i64) > 2))
Expand Down Expand Up @@ -33013,6 +33025,7 @@ pub fn emit_rust_tco_non_self_call(
let call_str = emit_typed_call(
f.clone(),
frame.expr.clone().children.clone(),
frame.expr.clone().inferred.clone(),
cs.clone(),
call_semantics_is_function_value(cs.clone()),
crate::v1_std_core::call_semantics_target(cs.clone()),
Expand Down
2 changes: 1 addition & 1 deletion src/v1/stage0/src/v1_compiler_runtime_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn rt_rc_container_ops() -> String {
}

pub fn rt_filesystem() -> String {
v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(crate::extdeps_filesystem_rust_realization::rust_file_create_new_canonical_block(), "\n".to_string()), "#[derive(Debug, Clone)]\n".to_string()), "pub struct FilesystemReadResult {\n".to_string()), " pub content: String,\n".to_string()), "}\n\n".to_string()), "pub fn filesystem_read(path: String) -> FilesystemReadResult {\n".to_string()), " let content = std::fs::read_to_string(&path)\n".to_string()), " .unwrap_or_else(|e| panic!(\"failed to read {}: {}\", path, e));\n".to_string()), " FilesystemReadResult { content }\n".to_string()), "}\n".to_string())
v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(crate::extdeps_filesystem_rust_realization::rust_file_create_new_canonical_block(), "\n".to_string()), "#[derive(Debug, Clone)]\n".to_string()), "pub struct FilesystemReadResult {\n".to_string()), " pub content: String,\n".to_string()), "}\n\n".to_string()), "pub fn unrealized_host_seam<T>(seam: &str) -> T {\n".to_string()), " panic!(\"{}\", seam);\n".to_string()), "}\n\n".to_string()), "pub fn filesystem_read(path: String) -> FilesystemReadResult {\n".to_string()), " let content = std::fs::read_to_string(&path)\n".to_string()), " .unwrap_or_else(|e| panic!(\"failed to read {}: {}\", path, e));\n".to_string()), " FilesystemReadResult { content }\n".to_string()), "}\n".to_string())
}

pub fn rt_hash_ops() -> String {
Expand Down
4 changes: 4 additions & 0 deletions src/v1/stage0/src/v1_rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,10 @@ pub struct FilesystemReadResult {
pub content: String,
}

pub fn unrealized_host_seam<T>(seam: &str) -> T {
panic!("{}", seam);
}

pub fn filesystem_read(path: String) -> FilesystemReadResult {
let content =
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read {}: {}", path, e));
Expand Down
7 changes: 6 additions & 1 deletion src/v2/extdeps/languages/rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -2261,10 +2261,15 @@ fn rust_target_model() -> TargetModel {
}
}

// `LexPattern` declares `LiteralPattern { text: v2.std.text.String }` -- the QUALIFIED spelling, so
// the field is the free-monoid char list on purpose, not the target's native string. The constructor
// in `v2.std.compilers.lexing` converts with `chars` when it builds one from a fragment; this one
// took an ordinary string and handed it over unconverted, which rustc answered as a carrier mismatch
// at the field rather than at the missing conversion.
fn rust_lex_rule(token_class: Symbol, text: String) -> LexRule {
TokenRule {
token_class: token_class,
pattern: LiteralPattern { text: text }
pattern: LiteralPattern { text: chars(s: text) }
}
}

Expand Down
15 changes: 13 additions & 2 deletions src/v2/std/compilers/semantic_decl_emission.dag
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,20 @@ fn semantic_decl_emission_bundle_malformed_diagnostic(bundle: Node) -> Diagnosti
}
}

// THE FREE-MONOID ENCODING READS CODE POINTS, SO THE STRING IS VIEWED AS ONE HERE.
//
// The parameter is an ordinary string and its callers are right to pass one -- `semantic_decl_emit`
// hands it a `text` and a `spelling`. What the BODY needs is the char-list view, because it folds
// each element through `from_code_point`. `chars` is the conversion between the two, and it is the
// same one `v2.std.compilers.lexing` already uses when it builds a `LiteralPattern`.
//
// WITHOUT IT THE FOLD HAS NO ELEMENT TYPE TO INFER: `String` renders as the target's native string,
// `fold_list` is generic over `FreeMonoid<T>`, and rustc answers E0282 rather than naming the
// missing view. That diagnostic is what sent this to the `String`-fork program on first reading; it
// is a missing conversion at one site.
fn semantic_decl_string_to_bundle_node(s: String) -> Node {
fold_list(
xs: s,
xs: chars(s: s),
empty: target_model_type_atom_node(identity: ^free_monoid_variant_empty),
cons: fn(tail, c) {
target_model_type_conj_node(
Expand Down Expand Up @@ -284,7 +295,7 @@ fn decode_semantic_decl_lex_rule_row(row: Node) -> Outcome<LexRule> {
outcome_accepted(
value: TokenRule {
token_class: token_class,
pattern: LiteralPattern { text: text }
pattern: LiteralPattern { text: chars(s: text) }
}
)
}
Expand Down
Loading