From 6b5c74bed02ac7647e990b989024a717c8957fed Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 20 Jun 2026 17:49:19 +0000 Subject: [PATCH] fix(emit_rust): peel MachineWidth literal-Nat phantom args to () (E0107). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alias RHS sites like Compose> were rendering bare MachineWidth or MachineWidth<> because width-nat literals have no Rust leaf name. Ground parametric opaque carriers at applied-arg positions: literal Nat widths become (), token args like PointerWidth preserved. Emitter-completeness slice only — seed stays at main; seed-green in #5325. Adds ignored emit regression test (un-ignores when seed co-lands). E0107 bootstrap measurement: 10 → 0 in std_integer.rs. Co-authored-by: Cursor --- src/v1/05_emit_rust.dag | 63 +++++++++++++++++++-- src/v1/tests/src/width_nat_type_arg_test.rs | 38 +++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 399633b943c..bbbc9e285b5 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -114,7 +114,7 @@ import v1.compiler.infer_emit_info { variant_belongs_to_enum, variant_summary_key } import v1.compiler.resolve { get_exported_names } -import v1.compiler.infer_resolve { lookup_unit_variant_phantom_type } +import v1.compiler.infer_resolve { lookup_unit_variant_phantom_type, is_width_nat_type_literal } import v1.compiler.ownership { OwnershipProof, analyze_ownership, analyze_single_fold, build_movable_set, build_read_only_params, collect_callable_refs } import std.induction { SubValueUnknown } @@ -309,6 +309,37 @@ fn rust_normalize_partial_function_field_type_text(rendered: String) -> String { ) } +// MachineWidth<8> etc.: phantom width-nat args ground to () in Rust, not empty <> (E0107). +fn is_parametric_opaque_type_by_name(env: TypeEnv, type_name: String) -> Bool { + match lookup_type_by_name(env: env, name: type_name) { + Present { value: item } => is_parametric_opaque_type_decl_item(item: item, source_indices: env.source_indices) + Absent => false + } +} + +fn is_parametric_opaque_type_base(type_name: String, def_mod_filename: String, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> Bool { + match type_item_by_name_in_module_filename(rhs_name: type_name, mod_filename: def_mod_filename, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { + Present { value: item } => is_parametric_opaque_type_decl_item(item: item, source_indices: source_indices) + Absent => false + } +} + +fn render_rust_phantom_opaque_applied_type_arg(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, scope: InferScope, imports: List, registry: Map, module_name: String, export_sets: Map>, typed_modules: List, module_index: ModuleIndex, variant_to_enum: Map) -> String { + if is_width_nat_type_literal(n: n) { + "()" + } else { + render_rust_alias_rhs_type(n: n, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, scope: scope, imports: imports, registry: registry, module_name: module_name, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index, variant_to_enum: variant_to_enum) + } +} + +fn render_rust_phantom_opaque_applied_decl_arg(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { + if is_width_nat_type_literal(n: n) { + "()" + } else { + render_rust_decl_type(n: n, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) + } +} + fn render_rust_applied_type_arg(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { match n.inferred { Present { value: TypeVariable { id: tv } } => tv @@ -317,11 +348,19 @@ fn render_rust_applied_type_arg(n: Node, generic_param_names: List, shar } fn render_rust_applied_type(n: Node, generic_param_names: List, shared_types: Set, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { - let base = rust_named_type_base(name: authored_name_at(source_indices: source_indices, node: n), source_indices: source_indices) + let base_name = authored_name_at(source_indices: source_indices, node: n) + let base = rust_named_type_base(name: base_name, source_indices: source_indices) if n.children |> count == 0 { base } else { - let args = n.children |> map(arg => render_rust_applied_type_arg(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env)) |> join(separator: ", ") + let peel = is_parametric_opaque_type_by_name(env: env, type_name: base_name) + let args = n.children |> map(arg => + if peel { + render_rust_phantom_opaque_applied_decl_arg(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) + } else { + render_rust_applied_type_arg(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) + } + ) |> join(separator: ", ") concat(base, "<", args, ">") } } @@ -365,7 +404,14 @@ fn render_rust_decl_type(n: Node, generic_param_names: List, shared_type render_rust_shared_type_if_needed(type_name: name, rendered: name, shared_types: shared_types) } else { let base = rust_named_type_base(name: name, source_indices: source_indices) - let args = n.children |> map(arg => render_rust_decl_type(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env)) |> join(separator: ", ") + let peel = is_parametric_opaque_type_by_name(env: env, type_name: name) + let args = n.children |> map(arg => + if peel { + render_rust_phantom_opaque_applied_decl_arg(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) + } else { + render_rust_decl_type(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) + } + ) |> join(separator: ", ") let applied_ty = concat(base, "<", args, ">") render_rust_shared_type_if_needed(type_name: name, rendered: applied_ty, shared_types: shared_types) } @@ -454,7 +500,14 @@ fn render_rust_alias_rhs_type(n: Node, generic_param_names: List, shared } else { name } - let args = n.children |> map(arg => render_rust_alias_rhs_type(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, scope: scope, imports: imports, registry: registry, module_name: module_name, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index, variant_to_enum: variant_to_enum)) |> join(separator: ", ") + let peel = is_parametric_opaque_type_base(type_name: name, def_mod_filename: def_mod, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) + let args = n.children |> map(arg => + if peel { + render_rust_phantom_opaque_applied_type_arg(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, scope: scope, imports: imports, registry: registry, module_name: module_name, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index, variant_to_enum: variant_to_enum) + } else { + render_rust_alias_rhs_type(n: arg, generic_param_names: generic_param_names, shared_types: shared_types, source_indices: source_indices, scope: scope, imports: imports, registry: registry, module_name: module_name, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index, variant_to_enum: variant_to_enum) + } + ) |> join(separator: ", ") let applied_ty = concat(base, "<", args, ">") if set_contains(shared_types, name) && !rust_type_is_rc_wrapped(type_name: applied_ty) { wrap_shared_type(target: Rust, inner: applied_ty) diff --git a/src/v1/tests/src/width_nat_type_arg_test.rs b/src/v1/tests/src/width_nat_type_arg_test.rs index 966a76b9797..dbac53c96a7 100644 --- a/src/v1/tests/src/width_nat_type_arg_test.rs +++ b/src/v1/tests/src/width_nat_type_arg_test.rs @@ -78,6 +78,44 @@ fn v1_std_float_dag_resolves_with_literal_machine_width() { ); } +#[test] +#[ignore = "emitter seed on main lacks dag fix until #5325 co-land; E0107 10→0 verified via 2-stage bootstrap"] +fn machine_width_phantom_arg_rust_emit_peels_literal_width_to_unit() { + use v1_compiler::cli_run; + use v1_compiler::v1_compiler_artifact::RenderTarget; + use v1_compiler::v1_compiler_compile::compile_sources; + + let roots: Vec = crate::helpers::source_roots() + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect(); + let entry = crate::helpers::workspace_root() + .join("dsl/std/integer.dag") + .to_string_lossy() + .to_string(); + let sources = cli_run::load_sources_for_entry(&roots, &entry) + .unwrap_or_else(|e| panic!("failed to load {entry}: {e}")); + let result = compile_sources(std::rc::Rc::new(sources), RenderTarget::Rust); + let integer_rs = result + .files + .iter() + .find(|f| f.path.contains("std_integer.rs")) + .map(|f| f.content.as_str()) + .unwrap_or(""); + assert!( + integer_rs.contains("MachineWidth<()>"), + "literal MachineWidth should peel to MachineWidth<()> in Rust emit, got:\n{integer_rs}" + ); + assert!( + !integer_rs.contains("MachineWidth<>"), + "peeled MachineWidth must not emit empty angle brackets (E0107), got:\n{integer_rs}" + ); + assert!( + integer_rs.contains("MachineWidth"), + "PointerWidth token should stay as a type argument, got:\n{integer_rs}" + ); +} + #[test] fn machine_width_use_site_resolves_without_unresolved_type() { let source = r#"