diff --git a/dag/gunbc/bare_name_fork_lens.dag b/dag/gunbc/bare_name_fork_lens.dag index c6ae599e6e5..1c8532610e6 100644 --- a/dag/gunbc/bare_name_fork_lens.dag +++ b/dag/gunbc/bare_name_fork_lens.dag @@ -1,7 +1,7 @@ module gunbc.bare_name_fork_lens import std.types { Bool, Int, String } -import v2.std.decl_index { DeclFact, ItemKind, FnItem, FuncItem, TypeItem, DataItem, ServiceItem, OtherItem, decl_facts } +import v2.std.decl_index { DeclFact, ItemKind, FnItem, TypeItem, DataItem, ServiceItem, OtherItem, decl_facts } import std.materialization_ladder { string_list_contains } // WHAT THIS ANSWERS, AND WHY A COMPILE CANNOT. @@ -39,7 +39,6 @@ import std.materialization_ladder { string_list_contains } fn item_kind_text(kind: ItemKind) -> String { match kind { FnItem => "fn" - FuncItem => "fn" TypeItem => "type" DataItem => "data" ServiceItem => "service" diff --git a/dag/gunbc/claim_batch_function_preflight_seed_growth.dag b/dag/gunbc/claim_batch_function_preflight_seed_growth.dag index 2750fee5cc4..ad134eba625 100644 --- a/dag/gunbc/claim_batch_function_preflight_seed_growth.dag +++ b/dag/gunbc/claim_batch_function_preflight_seed_growth.dag @@ -14,7 +14,7 @@ data claim_batch_function_preflight_seed_growth_justification: SeedGrowthJustifi hand_authored_declarations: [ DeclarationRef { module_path: "v1_compiler.claim_batch", decl_name: "validate_explicit_functions", field: WholeDeclaration } ], - reason: "WHY RUST IS STILL NEEDED: claim_batch is a seed-host CLI and its argv is consumed before any .dag evaluation frame exists. validate_explicit_functions adds no declaration vocabulary and no selection policy: it asks module_path_index.parse_dag_file, the canonical tokenizer/parser path, for the entry's constructed Node items and retains exactly ItemKind.FnItem or ItemKind.FuncItem, the same callable partition used by the interpreter. A parse failure refuses; it never becomes an empty declaration set. The function exists only to place the evaluator's existing no-such-function refusal before whole-corpus preparation.", + reason: "WHY RUST IS STILL NEEDED: claim_batch is a seed-host CLI and its argv is consumed before any .dag evaluation frame exists. validate_explicit_functions adds no declaration vocabulary and no selection policy: it asks module_path_index.parse_dag_file, the canonical tokenizer/parser path, for the entry's constructed Node items and retains exactly ItemKind.FnItem, the same callable partition used by the interpreter. A parse failure refuses; it never becomes an empty declaration set. The function exists only to place the evaluator's existing no-such-function refusal before whole-corpus preparation.", owning_dissolution_lane: "v1-hand-queue-drain" as RoadmapNodeId, trigger: "Delete validate_explicit_functions and this row when claim_batch argv ingestion is modeled in .dag and function selections are constructed as declaration identities before the seed host receives an execution plan.", current_boundary: "Only explicit --function/--functions rows are checked, immediately after path argv validation and before the bare-reference warm. Discovery-produced rows retain their existing producer-owned identity path. A declared callable proceeds unchanged; a missing name or an entry that cannot be parsed exits 2 before any corpus preparation." diff --git a/dag/gunbc/instruments/multi_module_compile_fixture.dag b/dag/gunbc/instruments/multi_module_compile_fixture.dag index 9552e8a2c90..fbc45ad0ce7 100644 --- a/dag/gunbc/instruments/multi_module_compile_fixture.dag +++ b/dag/gunbc/instruments/multi_module_compile_fixture.dag @@ -70,7 +70,7 @@ import gunbc.compile_diagnostic_census { CompileDiagnosticCensusRow } // SIGNATURE SHAPE IS OBSERVABLE AS A STRUCTURED RUST PROJECTION OVER THE RESOLVED // REGISTRY, not as contents-by-path and not as a read of emitted bytes. On // FixtureCompileCompleted the field resolved_rust_functions carries List -// — per FnItem/FuncItem in item_registry: source owner_module + declaration_name and the +// — per FnItem in item_registry: source owner_module + declaration_name and the // ordered parameter name list derived from that registry row (emit_ident / service_var_name // transforms applied). Witnesses assert over that list; raw text is not offered as an assertion // surface. Discriminating RED: test.claim.resolved_rust_fn_projection_witness. @@ -90,7 +90,7 @@ type MultiModuleCompileFixture { entry: NonEmptyStr } -// THE STRUCTURED PROJECTION WITNESSES ASSERT OVER. Per resolved FnItem/FuncItem on each +// THE STRUCTURED PROJECTION WITNESSES ASSERT OVER. Per resolved FnItem on each // TypedModule.item_registry (NOT the bare-name-merged ResolvedGraph.item_registry): source // identity (owning module + declaration name) and the PARAMETER NAME LIST taken from ItemInfo // with the same per-arm name transforms emit_func_params applies today (emit_ident for authored diff --git a/dag/test/claim/effectful_item_kind_collapse_witness_test.dag b/dag/test/claim/effectful_item_kind_collapse_witness_test.dag new file mode 100644 index 00000000000..bb073a7aaab --- /dev/null +++ b/dag/test/claim/effectful_item_kind_collapse_witness_test.dag @@ -0,0 +1,97 @@ +module test.claim.effectful_item_kind_collapse_witness_test + +import std.types { String, Bool, Int } +import v2.std.optional { Present, Absent } +import gunbc.compile_diagnostic_census { CompileDiagnosticCensus, CensusObserved, CensusNotRunnable, census_blocking_rows, census_total_count } +import gunbc.guarantee_probe_corpus { census_blocking_count_for_class } + +// ENROLLED EVIDENCE for the FuncItem -> FnItem collapse (DESIGN section 3: one concept, one +// name) and for the two facts that collapse made observable. `ItemKind.FuncItem` was the node +// model's shadow of the deleted `func` spelling; its only distinguishing content was that the +// registry projection (`v1.compiler.infer` build_item_info) hardcoded is_self_recursive and +// has_non_tail_self_call false for every effectful item, and that the zero-parameter branch of +// infer_item -- the branch a `func` with no parameters landed in -- rebuilt the typed node with +// `uses: []`, extended no scope with the declared resources, and hardcoded the same two fields. +// After the collapse one arm builds every function's ItemInfo and one branch of infer_item types +// every function with a body, so the typed node and the registry answer the same question the +// same way. Neither of those facts is visible from a green over the existing corpus: no corpus +// module both declares a zero-parameter effectful function that reads its resource and is emitted +// to Rust, and no corpus module carries an effectful self-call. These fixtures are the +// constructed RED / control pairs, per DESIGN section 4b(4). +// +// THE ZERO-PARAMETER PAIR. `zpe_reads_resource` declares `uses net` and returns `net.id`, so the +// resource must be BOUND in the body's scope for the compile to carry no blocking diagnostic, and +// the emitted Rust signature must carry the resource parameter (`net: &`), which emit_func_params +// renders from the typed node's `uses` and from nothing else. Before this change the first arm +// refused (`net` unbound: the zero-parameter branch extended no scope) and the second emitted a +// signature with no resource parameter while the registry said the item was effectful -- two +// contradictory observations of one declaration. MUTATION BAR: restore `uses: []` on the +// zero-parameter typed node and both arms go RED; restore the scope omission alone and the first +// goes RED. +// +// THE RECURSION PAIR. emit_func_def renders every effectful declaration as `async fn` and never +// consults is_tco_eligible or is_self_recursive, so an effectful self-call reached the target +// verbatim and rustc refused it (E0733) AFTER `gunbc compile` had reported success. That is the +// fabricated-plausible-output failure of DESIGN section 5, and it was unreachable by any refusal +// keyed on the registry, because the registry said every effectful item was non-recursive. +// `EffectfulSelfRecursionUnrealized` is now raised at the Rust emitter boundary from the SAME +// registry read the pure path's TCO decision consumes. Both the tail and the non-tail shape are +// refused: the effectful path realizes neither. MUTATION BAR: restore `is_self_recursive: false` +// in build_item_info (in v1_compiler_infer.rs -- these fixtures are compiled by the SEED BINARY, +// so the mutation must be made in the mirror, exactly as +// generic_effectful_declaration_wall_witness_test records) and both refusal arms go RED while the +// control stays green; delete the diagnostics fold in emit_rust and the same happens. +// +// THE CONTROL is the same shape with the self-call replaced by a call to a pure helper, which is +// the realization the diagnostic text recommends: it compiles clean and emits. A refusal that +// fired on every effectful declaration would pass both refusal arms and FAIL this one. +// +// COVERAGE BOUND. These arms reach the emitted TEXT and the diagnostic census; none invokes +// rustc. The E0733 claim above is rustc's documented rule, cited as the reason the refusal +// exists, not measured here. + +data zpe_reads_resource_source: String = "module zpe_reads_resource\n\ntype ZpeNet \{\n id: Int\n}\n\nfn zpe_read() -> Int\n uses net: ZpeNet\n\{\n net.id\n}\n" + +data efr_tail_source: String = "module efr_tail\n\ntype EfrNet \{\n id: Int\n}\n\nfn efr_count_down(n: Int) -> Int\n uses net: EfrNet\n\{\n if n == 0 \{ net.id } else \{ efr_count_down(n: n - 1) }\n}\n" + +data efr_non_tail_source: String = "module efr_non_tail\n\ntype EfrNet \{\n id: Int\n}\n\nfn efr_sum_down(n: Int) -> Int\n uses net: EfrNet\n\{\n if n == 0 \{ net.id } else \{ n + efr_sum_down(n: n - 1) }\n}\n" + +data efr_control_source: String = "module efr_control\n\ntype EfrNet \{\n id: Int\n}\n\nfn efr_pure_sum(n: Int) -> Int \{\n if n == 0 \{ 0 } else \{ n + efr_pure_sum(n: n - 1) }\n}\n\nfn efr_effectful_total(n: Int) -> Int\n uses net: EfrNet\n\{\n net.id + efr_pure_sum(n: n)\n}\n" + +// NOT-RUNNABLE IS NOT ZERO: -1 satisfies neither the == 0 control nor the == 1 refusal. +fn eik_blocking_count(source: String, wanted: String) -> Int { + match census_blocking_count_for_class(census: compile_dag_diagnostic_census(source), wanted: wanted) { + Present { value: n } => n + Absent => 0 - 1 + } +} + +// TOTAL over every blocking class is exact here because each fixture imports nothing, so its +// closure is itself (see generic_effectful_declaration_wall_witness_test for why that matters). +fn eik_total_blocking(source: String) -> Int { + match compile_dag_diagnostic_census(source) { + CensusObserved { rows: rows } => census_total_count(rows: census_blocking_rows(rows: rows)) + CensusNotRunnable { cause: _ } => 0 - 1 + } +} + +test fn a_zero_parameter_effectful_declaration_binds_its_resource() -> Bool { + eik_total_blocking(source: zpe_reads_resource_source) == 0 +} + +test fn a_zero_parameter_effectful_declaration_emits_its_resource_parameter() -> Bool { + compile_dag_rust_emit_check(zpe_reads_resource_source, "src/zpe_reads_resource.rs", ["net: &"], []) +} + +test fn an_effectful_tail_self_call_is_refused_at_the_rust_emitter() -> Bool { + eik_blocking_count(source: efr_tail_source, wanted: "EffectfulSelfRecursionUnrealized") == 1 +} + +test fn an_effectful_non_tail_self_call_is_refused_at_the_rust_emitter() -> Bool { + eik_blocking_count(source: efr_non_tail_source, wanted: "EffectfulSelfRecursionUnrealized") == 1 +} + +test fn an_effectful_declaration_calling_a_pure_recursive_helper_is_admitted() -> Bool { + eik_total_blocking(source: efr_control_source) == 0 + && compile_dag_rust_emit_check(efr_control_source, "src/efr_control.rs", ["net: &"], ["compile_error!"]) +} diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 0c61cdbe097..3844ebd1839 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -429,6 +429,7 @@ type CompilerDiagnostic | DataReferenceVisibilityBudgetExceeded { name: String, span: SourceSpan } | ParameterDefaultFormNotAdmitted { parameter: String, admitted: List, span: SourceSpan } | AmbiguousAnonymousRecordLiteral { candidates: List, span: SourceSpan } + | EffectfulSelfRecursionUnrealized { name: String, span: SourceSpan } | ModuleFilenameCollision { filename: String, modules: List, span: SourceSpan } | EffectSummaryIncompleteAtFunctionValue { caller: String, span: SourceSpan } | EffectSummaryIncompleteAtLocalBinding { caller: String, name: String, span: SourceSpan } @@ -650,6 +651,7 @@ fn diagnostic_to_span(d: CompilerDiagnostic) -> SourceSpan { DataReferenceVisibilityBudgetExceeded { name: _, span: s } => s ParameterDefaultFormNotAdmitted { parameter: _, admitted: _, span: s } => s AmbiguousAnonymousRecordLiteral { candidates: _, span: s } => s + EffectfulSelfRecursionUnrealized { name: _, span: s } => s ModuleFilenameCollision { filename: _, modules: _, span: s } => s EffectSummaryIncompleteAtFunctionValue { caller: _, span: s } => s EffectSummaryIncompleteAtLocalBinding { caller: _, name: _, span: s } => s @@ -749,6 +751,7 @@ fn diagnostic_to_message(d: CompilerDiagnostic) -> String { ParameterDefaultFormNotAdmitted { parameter: p, admitted: forms, span: _ } => concat("default value for parameter '", p, "' is not an admitted form (admitted: ", join(forms, separator: ", "), ")") AmbiguousAnonymousRecordLiteral { candidates: cs, span: _ } => concat("ambiguous anonymous record literal shape matches ", to_string(value: cs |> count), " structs: ", join(cs, separator: ", "), " — add a nominal type") + EffectfulSelfRecursionUnrealized { name: n, span: _ } => concat("effectful declaration '", n, "' calls itself: the Rust realization renders an effectful declaration as `async fn`, and rustc refuses recursion in an async fn without boxing (E0733), which no emitter performs. Realize the recursion as a loop, or move the self-call into a pure helper.") ModuleFilenameCollision { filename: f, modules: ms, span: _ } => concat("module filename collision: ", to_string(value: ms |> count), " modules render one emitted file name '", f, "': ", join(ms, separator: ", "), " — module_to_filename maps '.' to '_', so these names are indistinguishable at the emitted path; rename one module segment") EffectSummaryIncompleteAtFunctionValue { caller: c, span: _ } => concat("effect summary incomplete: ", c, " calls through a function value, whose callee is chosen at runtime, so its effects are unknown rather than empty — the caller's summary is a lower bound, not the answer") EffectSummaryIncompleteAtLocalBinding { caller: c, name: n, span: _ } => concat(concat(concat(concat("effect summary incomplete: ", c), " calls the local binding '"), n), "', whose effects this pass cannot join through the registry, so the caller's summary is a lower bound rather than the answer") @@ -880,6 +883,7 @@ fn diagnostic_disposition(d: CompilerDiagnostic) -> DiagnosticDisposition { DataReferenceVisibilityBudgetExceeded { name: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } ParameterDefaultFormNotAdmitted { parameter: _, admitted: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } AmbiguousAnonymousRecordLiteral { candidates: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } + EffectfulSelfRecursionUnrealized { name: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } ModuleFilenameCollision { filename: _, modules: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } EffectSummaryIncompleteAtFunctionValue { caller: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } EffectSummaryIncompleteAtLocalBinding { caller: _, name: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 95843f44a0b..6bd0bcbc811 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -185,7 +185,7 @@ import v1.compiler.infer_emit_info { add_emit_item_summary, derive_variant_to_enum, close_fn_fields } import v1.compiler.infer_items { - ItemKind, FnItem, FuncItem, TypeItem, DataItem, ServiceItem, OtherItem, + ItemKind, FnItem, TypeItem, DataItem, ServiceItem, OtherItem, ItemInfo, TypedModule, TypedGraph, ResolvedGraph, ModuleInterface, leaf_owner_modules_from_registry, ModuleTypecheckProgress, ItemsChecked, AbandonedBeforeItems, inferred_to_outputs, item_kind @@ -11271,7 +11271,7 @@ fn infer_item(item: Node, scope: InferScope) -> TypedItemResult { diagnostics: concat(transport_diags, props_diags) } } - else if item.body != none && item.params |> count > 0 { + else if item.body != none && (item.params |> count > 0 || item.inferred != none) { let fn_scope = build_params_scope(scope: scope, params: item.params) let fn_scope = fold(item.uses, init: fn_scope, f: (s, u) => extend_scope(scope: s, name: resource_use_name_at(n: u, source_indices: scope.type_env.source_indices), resolved: resource_use_resource(n: u), provenance: SubValueUnknown) @@ -11317,39 +11317,6 @@ fn infer_item(item: Node, scope: InferScope) -> TypedItemResult { diagnostics: concat(transport_diags, props_diags, concat(body_diags, return_conformance_diags)) } } - else if item.body != none && item.params |> count == 0 && item.inferred != none { - let fn_decl_name = authored_name_at(source_indices: scope.type_env.source_indices, node: item) - let fn_scope = InferScope { - type_env: scope.type_env, - func_env: scope.func_env, - locals: scope.locals, - body_locals: scope.body_locals, - match_bound_names: scope.match_bound_names, - module_name: scope.module_name, - service_registry: scope.service_registry, - item_registry: scope.item_registry, - caller_decl_name: fn_decl_name, - lambda_param_provenance: scope.lambda_param_provenance - } - let body_result = infer_expr(texpr: item.body.value, scope: fn_scope, expected: Present { value: resolved_type(n: item) }) - let body_typed = body_result.typed - let body_diags = concat( - body_result.diagnostics, - concat( - declared_type_conformance_diags(declared: declared_return_type_node(item: item), produced: resolved_type(n: body_typed), span: item.body.value.span, scope: scope), - explicit_return_conformance_diags(declared: declared_return_type_node(item: item), body_typed: body_typed, scope: scope)) - ) - TypedItemResult { - item: Node { - occurrence_identity: item.occurrence_identity, name: item.name, span: item.span, ident_span: item.ident_span, children: [], params: [], - inferred: item.inferred, return_cardinality: item.return_cardinality, uses: [], body: Present { value: body_typed }, - connective: NoConnective, transport: typed_transport, - properties: typed_properties, type_annotation: typed_anno, - is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, module_item_kind: item.module_item_kind, expr_data: NoExprData - }, - diagnostics: concat(transport_diags, props_diags, body_diags) - } - } else if item.body != none && item.params |> count == 0 { let data_expected = if item.type_annotation != none { Present { value: item.type_annotation.value } } else { none } let val_result = infer_expr(texpr: item.body.value, scope: scope, expected: data_expected) @@ -11866,7 +11833,6 @@ fn interface_cache_from_module(cache: TypeEnvCache) -> TypeEnvCache { fn export_kind_of_item_kind(kind: ItemKind) -> ExportKind? { match kind { FnItem => Present { value: ExportFn } - FuncItem => Present { value: ExportFn } TypeItem => Present { value: ExportType } DataItem => Present { value: ExportData } ServiceItem => Present { value: ExportService } @@ -13251,17 +13217,6 @@ fn build_item_info(item: Node, source_indices: Map, module let res_names = item.uses |> map(u => resource_use_name_at(n: u, source_indices: source_indices)) let item_name_str = authored_name_at(source_indices: source_indices, node: item) match kind { - FuncItem => - ItemInfo { - name: item_name_str, - module_name: module_name, - kind: kind, - service_names: if item.body == none { [] } else { collect_typed_service_calls(texpr: item.body.value, source_indices: source_indices) }, - resource_names: res_names, - params: item.params, - is_self_recursive: false, - has_non_tail_self_call: false - } FnItem => ItemInfo { name: item_name_str, diff --git a/src/v1/04_items.dag b/src/v1/04_items.dag index 5af091ffead..014f14fd20c 100644 --- a/src/v1/04_items.dag +++ b/src/v1/04_items.dag @@ -47,7 +47,7 @@ import v1.compiler.infer_emit_info { } -type ItemKind = FnItem | FuncItem | TypeItem | DataItem | ServiceItem | OtherItem +type ItemKind = FnItem | TypeItem | DataItem | ServiceItem | OtherItem type ItemInfo { name: String @@ -243,8 +243,6 @@ fn item_kind(item: Node) -> ItemKind { TypeItem } else if item.transport != none { ServiceItem - } else if item.body != none && item.uses |> count > 0 { - FuncItem } else if item.body != none && item.params |> count > 0 { FnItem } else if item.body != none && item.type_annotation != none { diff --git a/src/v1/04_lookup.dag b/src/v1/04_lookup.dag index 43770ffa43c..27b5a4f6a43 100644 --- a/src/v1/04_lookup.dag +++ b/src/v1/04_lookup.dag @@ -1,7 +1,7 @@ module v1.compiler.infer_lookup import std.induction { SubValueRelation } -import v1.compiler.infer_items { ItemKind, FnItem, FuncItem, TypeItem, ServiceItem, DataItem, OtherItem, item_kind } +import v1.compiler.infer_items { ItemKind, FnItem, TypeItem, ServiceItem, DataItem, OtherItem, item_kind } import std.types { is_kernel_type } import std.decl_ref { DeclarationRef, decl_ref } import std.primitive_projection { @@ -303,9 +303,9 @@ fn resolved_declaration_call_target(declared: DeclaredCallableIdentity) -> CallT // reads "declared" -- the authority-substitution geometry the outcome type exists to remove. // // THE KIND GATE EXCLUDES WHAT IS NOT CALLABLE; IT DOES NOT DEMAND PROOF OF WHAT IS. -// `item_kind` reads FnItem and FuncItem off the presence of a BODY, and a census node is a +// `item_kind` reads FnItem off the presence of a BODY, and a census node is a // borrowed declaration surface that carries a signature without one -- so a nullary fn reached -// this way reads as OtherItem, and an admit-list of the two callable kinds refuses every member +// this way reads as OtherItem, and an admit-list of the callable kind refuses every member // of the population this arm exists to serve. The call position is what establishes that the name // is being called; what the kind can still tell us is that the declaration is a TYPE, a SERVICE or // a DATA row, none of which a call may target. diff --git a/src/v1/04_service.dag b/src/v1/04_service.dag index 33393ab4025..2edb12683e8 100644 --- a/src/v1/04_service.dag +++ b/src/v1/04_service.dag @@ -27,7 +27,7 @@ import v1.compiler.infer_types { } import v1.compiler.infer_items { - ItemInfo, ItemKind, FuncItem, TypedModule, + ItemInfo, ItemKind, TypedModule, inferred_to_outputs } diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 20269f66951..e6e39718a90 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -40,7 +40,7 @@ import v1.std.core { PlainMethodSemantics, AlgebraMethodSemantics, ServiceMethodSemantics, MatchPattern, field_binding_pattern, TextFile, ErrorNode, make_error_node, InternalError, is_error_diagnostic, EmissionConstructUnprojectable, FilterInBranchCondition, - UnlistedImportUse, ReferenceDerivedImportProviderUnknown, ReferenceDerivedImportExportUnproven, AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, ParameterDefaultFormNotAdmitted, SourceSpan, resource_use_resource, UnaryOpKind, StringPart, Text, + UnlistedImportUse, ReferenceDerivedImportProviderUnknown, ReferenceDerivedImportExportUnproven, AmbiguousAnonymousRecordLiteral, EffectfulSelfRecursionUnrealized, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, ParameterDefaultFormNotAdmitted, SourceSpan, resource_use_resource, UnaryOpKind, StringPart, Text, Interpolation, transport_base_url, transport_has_auth, transport_auth_token, transport_auth_header_name, transport_headers, transport_env, transport_method, transport_path_template, transport_query, transport_request_body, transport_stdin, @@ -3339,6 +3339,7 @@ type EmitRustContext { workflow_funcs: List workflow_default_diags: List anonymous_record_diags: List + effectful_recursion_diags: List svc_module_map: Map test_projections: List export_sets: Map> @@ -3394,6 +3395,7 @@ fn build_emit_rust_context(typed: ResolvedGraph) -> EmitRustContext { let workflow_funcs = collect_workflow_funcs(modules: typed.modules, registry: registry, read_only_params_index: emit_info.read_only_params_index) let workflow_default_diags = validate_workflow_param_defaults(workflow_funcs: workflow_funcs) let anonymous_record_diags = typed.modules |> flat_map(tm => tm.items |> flat_map(item => ambiguous_anonymous_record_literal_diagnostics(n: item, type_summaries: emit_info.type_summaries, source_indices: tm.type_env.source_indices, module_name: authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module)))) + let effectful_recursion_diags = typed.modules |> flat_map(tm => effectful_self_recursion_diagnostics(tm: tm, registry: registry)) let svc_module_map = typed.modules |> fold(init: empty_map(), f: (acc, tm) => let svc_items = tm.items |> filter(item => item.module_item_kind == ModuleItemService) let mod_filename = module_to_filename(name: authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module)) @@ -3411,6 +3413,7 @@ fn build_emit_rust_context(typed: ResolvedGraph) -> EmitRustContext { workflow_funcs: workflow_funcs, workflow_default_diags: workflow_default_diags, anonymous_record_diags: anonymous_record_diags, + effectful_recursion_diags: effectful_recursion_diags, svc_module_map: svc_module_map, test_projections: test_projections, export_sets: export_sets, @@ -3496,6 +3499,10 @@ fn emit_rust_selected(typed: ResolvedGraph, selection: RustModuleRenderSelection if anonymous_record_diags |> count > 0 { return EmitResult { files: [], diagnostics: anonymous_record_diags } } + let effectful_recursion_diags = ctx.effectful_recursion_diags + if effectful_recursion_diags |> count > 0 { + return EmitResult { files: [], diagnostics: effectful_recursion_diags } + } let filename_collisions = module_filename_collision_diagnostics(typed: typed) if filename_collisions |> count > 0 { return EmitResult { files: [], diagnostics: filename_collisions } @@ -13465,6 +13472,33 @@ fn struct_candidates_by_field_names(field_names: List, type_summaries: M ) } +// THE EFFECTFUL PATH HAS NO RECURSION REALIZATION, SO IT REFUSES ONE RATHER THAN RENDERING IT. +// emit_func_def renders every effectful declaration as `async fn` and never consults +// is_tco_eligible or is_self_recursive -- the loop transform and the stacker guard belong to the +// pure path only -- so a self-call inside an effectful body would be emitted verbatim, and rustc +// refuses recursion in an async fn without boxing (E0733). Emitting it would be a green compile +// whose output cannot build (DESIGN section 5, fabricated plausible output). The fact is read from +// the SAME registry row the pure path's TCO decision reads, through the same reader, so this is +// the first consumer of is_self_recursive on an effectful declaration: until the FuncItem collapse +// the registry hardcoded that field false for every effectful item, and this refusal could not +// have fired. Rust-specific by construction: Python's `async def` and Go recurse without boxing. +fn effectful_self_recursion_diagnostics(tm: TypedModule, registry: Map) -> List { + let si = tm.type_env.source_indices + let module_name = authored_name_at(source_indices: si, node: tm.module) + tm.items |> flat_map(item => + match item.body { + Present { value: body } => + if item.uses |> count > 0 { + let name = authored_name_at(source_indices: si, node: item) + if is_self_recursive(id: DeclaredCallableIdentity { owner_module_path: module_name, decl_name: name }, body: body, registry: registry, source_indices: si) { + [make_error_node(diagnostic: EffectfulSelfRecursionUnrealized { name: name, span: item.span }, module_name: module_name)] + } else { [] } + } else { [] } + Absent => [] + } + ) +} + // gunbc dashboard node://adhoc-e47d0143-6a2. An anonymous record literal has no authored // nominal identity, so the Rust emitter may name a struct only when the literal's field shape // (and, when necessary, its inferred field-type hints) identifies exactly one TypeSummary. The @@ -13485,6 +13519,7 @@ fn struct_candidates_by_field_names(field_names: List, type_summaries: M // RUNG: mechanically preventable at the v1 emitter boundary, not structurally impossible in the // source language; the target compiler is never counted as part of this guarantee. + fn anonymous_record_struct_candidates(field_names: List, field_type_hints: Map, type_summaries: Map) -> List { let candidates = struct_candidates_by_field_names(field_names: field_names, type_summaries: type_summaries) if candidates |> count <= 1 { candidates } diff --git a/src/v1/stage0/src/bin/claim_batch.rs b/src/v1/stage0/src/bin/claim_batch.rs index 3336dd90ecb..c55218bde86 100644 --- a/src/v1/stage0/src/bin/claim_batch.rs +++ b/src/v1/stage0/src/bin/claim_batch.rs @@ -216,12 +216,7 @@ fn validate_explicit_functions(entry_groups: &[EntryGroup]) -> Result<(), ExitCo let declared: std::collections::BTreeSet = parsed .items .iter() - .filter(|item| { - matches!( - item_kind((*item).clone()), - ItemKind::FnItem | ItemKind::FuncItem - ) - }) + .filter(|item| matches!(item_kind((*item).clone()), ItemKind::FnItem)) .map(|item| item.name.clone()) .collect(); let missing: Vec<&str> = group diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 39d051603e7..8e5f2f883d0 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -20214,7 +20214,7 @@ pub fn run_bootstrap_dag_operation( let declaration_present = graph.item_registry.values().any(|item| { item.module_name == declaration_module && item.name == declaration_name - && matches!(item.kind, ItemKind::FnItem | ItemKind::FuncItem) + && matches!(item.kind, ItemKind::FnItem) }); if !declaration_present { eprintln!( @@ -33925,7 +33925,7 @@ fn cla_compute_wildcard_facts(roots: &[String]) -> Vec Vec { let closed = non_fold_residue_closed_coproduct_type_names(); let mut out = Vec::new(); for fact in &walk.facts { - if !matches!(fact.kind, ItemKind::FnItem | ItemKind::FuncItem) { + if !matches!(fact.kind, ItemKind::FnItem) { continue; } let Some(body) = fact.node.body.as_ref() else { @@ -40391,7 +40391,6 @@ pub fn item_kind_census_label(kind: &crate::v1_compiler_infer_items::ItemKind) - use crate::v1_compiler_infer_items::ItemKind; match kind { ItemKind::FnItem => "fn", - ItemKind::FuncItem => "func", ItemKind::TypeItem => "type", ItemKind::DataItem => "data", ItemKind::ServiceItem => "service", diff --git a/src/v1/stage0/src/cli_run/compile_clean.rs b/src/v1/stage0/src/cli_run/compile_clean.rs index dd074e7ca1c..9614f452973 100644 --- a/src/v1/stage0/src/cli_run/compile_clean.rs +++ b/src/v1/stage0/src/cli_run/compile_clean.rs @@ -1067,6 +1067,7 @@ pub fn compile_clean_diagnostic_class_specimen() -> Vec { DataReferenceVisibilityBudgetExceeded { name: s(), span: no_span() }, ParameterDefaultFormNotAdmitted { parameter: s(), admitted: istrings(), span: no_span() }, AmbiguousAnonymousRecordLiteral { candidates: istrings(), span: no_span() }, + EffectfulSelfRecursionUnrealized { name: s(), span: no_span() }, ModuleFilenameCollision { filename: s(), modules: istrings(), span: no_span() }, EffectSummaryIncompleteAtFunctionValue { caller: s(), span: no_span() }, EffectSummaryIncompleteAtLocalBinding { caller: s(), name: s(), span: no_span() }, @@ -1547,6 +1548,9 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::AmbiguousAnonymousRecordLiteral { .. } => { "AmbiguousAnonymousRecordLiteral" } + CompilerDiagnostic::EffectfulSelfRecursionUnrealized { .. } => { + "EffectfulSelfRecursionUnrealized" + } CompilerDiagnostic::ModuleFilenameCollision { .. } => "ModuleFilenameCollision", CompilerDiagnostic::EffectSummaryIncompleteAtFunctionValue { .. } => { "EffectSummaryIncompleteAtFunctionValue" @@ -1646,6 +1650,7 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::AmbiguousAnonymousRecordLiteral { candidates, .. } => { candidates.iter().cloned().collect::>().join("|") } + CompilerDiagnostic::EffectfulSelfRecursionUnrealized { name, .. } => name.clone(), CompilerDiagnostic::ModuleFilenameCollision { filename, .. } => filename.clone(), CompilerDiagnostic::EffectSummaryIncompleteAtFunctionValue { caller, .. } => caller.clone(), CompilerDiagnostic::EffectSummaryIncompleteAtLocalBinding { caller, .. } => caller.clone(), diff --git a/src/v1/stage0/src/cli_run/complexity_gates.rs b/src/v1/stage0/src/cli_run/complexity_gates.rs index 978caa3a685..dba50fa0cbb 100644 --- a/src/v1/stage0/src/cli_run/complexity_gates.rs +++ b/src/v1/stage0/src/cli_run/complexity_gates.rs @@ -220,7 +220,7 @@ pub fn complexity_linearity_audit_corpus_over_decl_facts( summary.files_parsed = walk.files_parsed; for fact in &walk.facts { - if !matches!(fact.kind, ItemKind::FnItem | ItemKind::FuncItem) { + if !matches!(fact.kind, ItemKind::FnItem) { continue; } summary.fns_scanned += 1; diff --git a/src/v1/stage0/src/cli_run/emit_host.rs b/src/v1/stage0/src/cli_run/emit_host.rs index b22d132fa04..3419c11847c 100644 --- a/src/v1/stage0/src/cli_run/emit_host.rs +++ b/src/v1/stage0/src/cli_run/emit_host.rs @@ -524,7 +524,7 @@ pub fn compile_dag_multi_module_fixture( } } -/// Resolved-registry projection for the Rust emit target: one row per `FnItem` / `FuncItem` in +/// Resolved-registry projection for the Rust emit target: one row per `FnItem` in /// each `TypedModule.item_registry` (not the bare-name-merged `ResolvedGraph.item_registry`). /// `ordered_parameter_names` applies `emit_ident(..., Rust)` on authored params and resource-use /// names, then `service_var_name` per service, concatenated in that order — the same per-arm @@ -567,7 +567,7 @@ fn project_resolved_rust_fn_signatures( let mut bare_fn_module_count: HashMap = HashMap::new(); for typed in graph.modules.iter() { for local in typed.item_registry.values() { - if matches!(local.kind, ItemKind::FnItem | ItemKind::FuncItem) { + if matches!(local.kind, ItemKind::FnItem) { *bare_fn_module_count.entry(local.name.clone()).or_insert(0) += 1; } } @@ -576,7 +576,7 @@ fn project_resolved_rust_fn_signatures( for typed in graph.modules.iter() { for local in typed.item_registry.values() { match local.kind { - ItemKind::FnItem | ItemKind::FuncItem => { + ItemKind::FnItem => { if bare_fn_module_count.get(&local.name).copied().unwrap_or(0) > 1 { continue; } @@ -1348,7 +1348,7 @@ pub fn transport_script_position_facts_for_path( let mut facts = Vec::new(); for item in items.iter() { let kind = item_kind(item.clone()); - if !matches!(kind, ItemKind::FuncItem | ItemKind::FnItem) { + if !matches!(kind, ItemKind::FnItem) { continue; } let Some(body) = item.body.as_ref() else { diff --git a/src/v1/stage0/src/coproduct_reflection.rs b/src/v1/stage0/src/coproduct_reflection.rs index db9b6c0ee5d..890af3bdadf 100644 --- a/src/v1/stage0/src/coproduct_reflection.rs +++ b/src/v1/stage0/src/coproduct_reflection.rs @@ -1534,7 +1534,7 @@ pub fn eval_fn_arrow_decl_facts_live( let (fn_decls, _module_count) = decls_parse_only_fail_closed( &abs_pool_roots, &pool_files, - &[ItemKind::FnItem, ItemKind::FuncItem], + &[ItemKind::FnItem], "fn_arrow_decl_facts_live", ) .map_err(|msg| InterpError::TypeError { msg })?; @@ -1882,11 +1882,10 @@ pub fn decl_facts_corpus_walk(pool_roots: &[String]) -> DeclFactsCorpusWalk { out.sort_by(|a, b| { let kind_ord = |k: ItemKind| match k { ItemKind::FnItem => 0, - ItemKind::FuncItem => 1, - ItemKind::TypeItem => 2, - ItemKind::DataItem => 3, - ItemKind::ServiceItem => 4, - ItemKind::OtherItem => 5, + ItemKind::TypeItem => 1, + ItemKind::DataItem => 2, + ItemKind::ServiceItem => 3, + ItemKind::OtherItem => 4, }; (a.rel_path.as_str(), a.name.as_str(), kind_ord(a.kind)).cmp(&( b.rel_path.as_str(), @@ -1909,7 +1908,6 @@ pub fn decl_facts_for_roots(pool_roots: &[String]) -> Vec { fn marshal_decl_item_kind(ctx: &InterpContext, kind: ItemKind) -> Value { let variant = match kind { ItemKind::FnItem => "FnItem", - ItemKind::FuncItem => "FuncItem", ItemKind::TypeItem => "TypeItem", ItemKind::DataItem => "DataItem", ItemKind::ServiceItem => "ServiceItem", @@ -1931,7 +1929,7 @@ fn marshal_decl_fact_node( ) -> InterpResult { match kind { ItemKind::TypeItem => concept_decl_node(ctx, si, item), - ItemKind::FnItem | ItemKind::FuncItem => { + ItemKind::FnItem => { Ok(fn_arrow_output_skeleton(ctx, si, item).unwrap_or_else(|| unit_type_node(ctx))) } ItemKind::DataItem => { @@ -1985,7 +1983,7 @@ fn marshal_export_signature_node( ) -> InterpResult { match kind { ItemKind::TypeItem => concept_decl_node(ctx, si, item), - ItemKind::FnItem | ItemKind::FuncItem => marshal_fn_export_signature_node(ctx, si, item), + ItemKind::FnItem => marshal_fn_export_signature_node(ctx, si, item), ItemKind::DataItem => match item .inferred .as_ref() diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index f4e4056a0ad..7717f67c09f 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -261,8 +261,9 @@ use crate::v1_std_core::CallTargetIdentity::{ use crate::v1_std_core::Cardinality::{CardOptional, Required}; use crate::v1_std_core::CompilerDiagnostic::{ AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, - EmissionConstructUnprojectable, InternalError, ParameterDefaultFormNotAdmitted, - ReferenceDerivedImportExportUnproven, ReferenceDerivedImportProviderUnknown, UnlistedImportUse, + EffectfulSelfRecursionUnrealized, EmissionConstructUnprojectable, InternalError, + ParameterDefaultFormNotAdmitted, ReferenceDerivedImportExportUnproven, + ReferenceDerivedImportProviderUnknown, UnlistedImportUse, }; use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::ExprData::{ @@ -6421,6 +6422,7 @@ pub struct EmitRustContext { pub workflow_funcs: Rc>>, pub workflow_default_diags: Rc>>, pub anonymous_record_diags: Rc>>, + pub effectful_recursion_diags: Rc>>, pub svc_module_map: Rc>, pub test_projections: Rc>>, pub export_sets: Rc>>>, @@ -6528,6 +6530,17 @@ pub fn build_emit_rust_context(typed: Rc) -> Rc } __result }); + let effectful_recursion_diags = Rc::new({ + let mut __result = Vec::new(); + for tm in typed.modules.clone().iter().cloned() { + __result.extend( + (*effectful_self_recursion_diagnostics(tm.clone(), registry.clone())) + .iter() + .cloned(), + ); + } + __result + }); let svc_module_map = typed.modules.clone().iter().cloned().fold( v1_rt::rc_empty_map::(), |acc: Rc>, tm: Rc| { @@ -6574,6 +6587,7 @@ pub fn build_emit_rust_context(typed: Rc) -> Rc workflow_funcs: workflow_funcs.clone(), workflow_default_diags: workflow_default_diags.clone(), anonymous_record_diags: anonymous_record_diags.clone(), + effectful_recursion_diags: effectful_recursion_diags.clone(), svc_module_map: svc_module_map.clone(), test_projections: test_projections.clone(), export_sets: export_sets.clone(), @@ -6648,6 +6662,13 @@ pub fn emit_rust_selected( diagnostics: anonymous_record_diags.clone(), }); } + let effectful_recursion_diags = ctx.effectful_recursion_diags.clone(); + if ((effectful_recursion_diags.clone().len() as i64) > 0) { + return Rc::new(EmitResult { + files: Rc::new(vec![]), + diagnostics: effectful_recursion_diags.clone(), + }); + } let filename_collisions = crate::v1_compiler_emit_core_support::module_filename_collision_diagnostics( typed.clone(), @@ -30444,6 +30465,43 @@ pub fn struct_candidates_by_field_names( } } +pub fn effectful_self_recursion_diagnostics( + tm: Rc, + registry: Rc>>, +) -> Rc>> { + { + let si = tm.type_env.clone().source_indices.clone(); + let module_name = crate::v1_std_core::authored_name_at(si.clone(), tm.module.clone()); + Rc::new({ + let mut __result = Vec::new(); + for item in tm.items.clone().iter().cloned() { + __result.extend((*match item.body.clone() { + Some(body) => if ((item.uses.clone().len() as i64) > 0) { + { + let name = crate::v1_std_core::authored_name_at(si.clone(), item.clone()); +if crate::v1_compiler_emit::is_self_recursive(Rc::new(DeclaredCallableIdentity { + owner_module_path: module_name.clone(), + decl_name: name.clone(), +}), body.clone(), registry.clone(), si.clone()) { + Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::EffectfulSelfRecursionUnrealized { + name: name.clone(), + span: item.span.clone(), +}), module_name.clone())]) + } else { + Rc::new(vec![]) + } +} + } else { + Rc::new(vec![]) + }, + std::option::Option::None => Rc::new(vec![]), +}).iter().cloned()); + } + __result + }) + } +} + pub fn anonymous_record_struct_candidates( field_names: Rc>, field_type_hints: Rc>, diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 233777047c5..1080dd6fde2 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -118,7 +118,7 @@ pub use crate::v1_compiler_infer_env::{ UnitVariantContribution, }; use crate::v1_compiler_infer_items::ItemKind::{ - DataItem, FnItem, FuncItem, OtherItem, ServiceItem, TypeItem, + DataItem, FnItem, OtherItem, ServiceItem, TypeItem, }; use crate::v1_compiler_infer_items::ModuleTypecheckProgress::{AbandonedBeforeItems, ItemsChecked}; pub use crate::v1_compiler_infer_items::{ @@ -19636,7 +19636,8 @@ pub fn infer_item(item: Rc, scope: Rc) -> Rc }) } else { if ((item.body.clone() != std::option::Option::None) - && ((item.params.clone().len() as i64) > 0)) + && (((item.params.clone().len() as i64) > 0) + || (item.inferred.clone() != std::option::Option::None))) { { let fn_scope = build_params_scope(scope.clone(), item.params.clone()); @@ -19775,51 +19776,46 @@ pub fn infer_item(item: Rc, scope: Rc) -> Rc }) } } else { - if (((item.body.clone() != std::option::Option::None) + if ((item.body.clone() != std::option::Option::None) && ((item.params.clone().len() as i64) == 0)) - && (item.inferred.clone() != std::option::Option::None)) { { - let fn_decl_name = crate::v1_std_core::authored_name_at( - scope.type_env.clone().source_indices.clone(), - item.clone(), - ); - let fn_scope = Rc::new(InferScope { - type_env: scope.type_env.clone(), - func_env: scope.func_env.clone(), - locals: scope.locals.clone(), - body_locals: scope.body_locals.clone(), - match_bound_names: scope.match_bound_names.clone(), - module_name: scope.module_name.clone(), - service_registry: scope.service_registry.clone(), - item_registry: scope.item_registry.clone(), - caller_decl_name: fn_decl_name.clone(), - lambda_param_provenance: scope.lambda_param_provenance.clone(), - }); - let body_result = infer_expr( + let data_expected = + if (item.type_annotation.clone() != std::option::Option::None) { + Some(item.type_annotation.clone().clone().unwrap()) + } else { + std::option::Option::None + }; + let val_result = infer_expr( item.body.clone().clone().unwrap(), - fn_scope.clone(), - Some(crate::v1_compiler_infer_types::resolved_type(item.clone())), + scope.clone(), + data_expected.clone(), ); - let body_typed = body_result.typed.clone(); - let body_diags = v1_rt::concat( - body_result.diagnostics.clone(), - v1_rt::concat( - declared_type_conformance_diags( - declared_return_type_node(item.clone()), - crate::v1_compiler_infer_types::resolved_type( - body_typed.clone(), + let val_typed = val_result.typed.clone(); + let val_diags = + if (item.type_annotation.clone() != std::option::Option::None) { + v1_rt::concat( + val_result.diagnostics.clone(), + declared_type_conformance_diags( + item.type_annotation.clone().clone().unwrap(), + crate::v1_compiler_infer_types::resolved_type( + val_typed.clone(), + ), + item.body.clone().clone().unwrap().span.clone(), + scope.clone(), ), - item.body.clone().clone().unwrap().span.clone(), - scope.clone(), - ), - explicit_return_conformance_diags( - declared_return_type_node(item.clone()), - body_typed.clone(), - scope.clone(), - ), - ), - ); + ) + } else { + val_result.diagnostics.clone() + }; + let inferred_ret = + if (item.type_annotation.clone() != std::option::Option::None) { + Some(Rc::new(InferredNode::Resolved { + node: item.type_annotation.clone().clone().unwrap(), + })) + } else { + val_typed.inferred.clone() + }; Rc::new(TypedItemResult { item: Rc::new(Node { occurrence_identity: item.occurrence_identity.clone(), @@ -19828,10 +19824,10 @@ pub fn infer_item(item: Rc, scope: Rc) -> Rc ident_span: item.ident_span.clone(), children: Rc::new(vec![]), params: Rc::new(vec![]), - inferred: item.inferred.clone(), + inferred: inferred_ret.clone(), return_cardinality: item.return_cardinality.clone(), uses: Rc::new(vec![]), - body: Some(body_typed.clone()), + body: Some(val_typed.clone()), connective: Connective::NoConnective, transport: typed_transport.clone(), properties: typed_properties.clone(), @@ -19845,170 +19841,95 @@ pub fn infer_item(item: Rc, scope: Rc) -> Rc }), diagnostics: v1_rt::concat( v1_rt::concat(transport_diags.clone(), props_diags.clone()), - body_diags.clone(), + val_diags.clone(), ), }) } } else { - if ((item.body.clone() != std::option::Option::None) - && ((item.params.clone().len() as i64) == 0)) + if (((item.params.clone().len() as i64) > 0) + && (item.body.clone() == std::option::Option::None)) { - { - let data_expected = - if (item.type_annotation.clone() != std::option::Option::None) { - Some(item.type_annotation.clone().clone().unwrap()) - } else { - std::option::Option::None - }; - let val_result = infer_expr( - item.body.clone().clone().unwrap(), - scope.clone(), - data_expected.clone(), - ); - let val_typed = val_result.typed.clone(); - let val_diags = - if (item.type_annotation.clone() != std::option::Option::None) { - v1_rt::concat( - val_result.diagnostics.clone(), - declared_type_conformance_diags( - item.type_annotation.clone().clone().unwrap(), - crate::v1_compiler_infer_types::resolved_type( - val_typed.clone(), - ), - item.body.clone().clone().unwrap().span.clone(), - scope.clone(), - ), - ) - } else { - val_result.diagnostics.clone() - }; - let inferred_ret = - if (item.type_annotation.clone() != std::option::Option::None) { - Some(Rc::new(InferredNode::Resolved { - node: item.type_annotation.clone().clone().unwrap(), - })) - } else { - val_typed.inferred.clone() - }; - Rc::new(TypedItemResult { - item: Rc::new(Node { - occurrence_identity: item.occurrence_identity.clone(), - name: item.name.clone(), - span: item.span.clone(), - ident_span: item.ident_span.clone(), - children: Rc::new(vec![]), - params: Rc::new(vec![]), - inferred: inferred_ret.clone(), - return_cardinality: item.return_cardinality.clone(), - uses: Rc::new(vec![]), - body: Some(val_typed.clone()), - connective: Connective::NoConnective, - transport: typed_transport.clone(), - properties: typed_properties.clone(), - type_annotation: typed_anno.clone(), - is_self_recursive: false, - has_non_tail_self_call: false, - match_pattern: std::option::Option::None, - module_item_kind: item.module_item_kind.clone(), - expr_data: Rc::new(ExprData::NoExprData), - ident: None, + Rc::new(TypedItemResult { + item: Rc::new(Node { + occurrence_identity: item.occurrence_identity.clone(), + name: item.name.clone(), + span: item.span.clone(), + ident_span: item.ident_span.clone(), + children: Rc::new({ + let mut __result = Vec::new(); + for c in item.children.clone().iter().cloned() { + __result.push( + infer_item(c.clone(), scope.clone()).item.clone(), + ); + } + __result }), - diagnostics: v1_rt::concat( - v1_rt::concat(transport_diags.clone(), props_diags.clone()), - val_diags.clone(), - ), - }) - } + params: item.params.clone(), + inferred: if (item.inferred.clone() != std::option::Option::None) { + item.inferred.clone() + } else { + Some(Rc::new(InferredNode::Resolved { node: unit_type() })) + }, + return_cardinality: item.return_cardinality.clone(), + uses: item.uses.clone(), + body: std::option::Option::None, + connective: Connective::NoConnective, + transport: typed_transport.clone(), + properties: typed_properties.clone(), + type_annotation: typed_anno.clone(), + is_self_recursive: false, + has_non_tail_self_call: false, + match_pattern: std::option::Option::None, + module_item_kind: item.module_item_kind.clone(), + expr_data: Rc::new(ExprData::NoExprData), + ident: None, + }), + diagnostics: v1_rt::concat( + transport_diags.clone(), + props_diags.clone(), + ), + }) } else { - if (((item.params.clone().len() as i64) > 0) - && (item.body.clone() == std::option::Option::None)) - { - Rc::new(TypedItemResult { - item: Rc::new(Node { - occurrence_identity: item.occurrence_identity.clone(), - name: item.name.clone(), - span: item.span.clone(), - ident_span: item.ident_span.clone(), - children: Rc::new({ - let mut __result = Vec::new(); - for c in item.children.clone().iter().cloned() { - __result.push( - infer_item(c.clone(), scope.clone()).item.clone(), - ); - } - __result - }), - params: item.params.clone(), - inferred: if (item.inferred.clone() - != std::option::Option::None) - { - item.inferred.clone() - } else { - Some(Rc::new(InferredNode::Resolved { node: unit_type() })) - }, - return_cardinality: item.return_cardinality.clone(), - uses: item.uses.clone(), - body: std::option::Option::None, - connective: Connective::NoConnective, - transport: typed_transport.clone(), - properties: typed_properties.clone(), - type_annotation: typed_anno.clone(), - is_self_recursive: false, - has_non_tail_self_call: false, - match_pattern: std::option::Option::None, - module_item_kind: item.module_item_kind.clone(), - expr_data: Rc::new(ExprData::NoExprData), - ident: None, - }), - diagnostics: v1_rt::concat( - transport_diags.clone(), - props_diags.clone(), - ), - }) - } else { - Rc::new(TypedItemResult { - item: Rc::new(Node { - occurrence_identity: item.occurrence_identity.clone(), - name: item.name.clone(), - span: item.span.clone(), - ident_span: item.ident_span.clone(), - children: Rc::new({ - let mut __result = Vec::new(); - for c in item.children.clone().iter().cloned() { - __result.push( - infer_item(c.clone(), scope.clone()).item.clone(), - ); - } - __result - }), - params: item.params.clone(), - inferred: if (item.inferred.clone() - != std::option::Option::None) - { - item.inferred.clone() - } else { - Some(Rc::new(InferredNode::Resolved { node: unit_type() })) - }, - return_cardinality: item.return_cardinality.clone(), - uses: item.uses.clone(), - body: std::option::Option::None, - connective: item.connective.clone(), - transport: typed_transport.clone(), - properties: typed_properties.clone(), - type_annotation: typed_anno.clone(), - is_self_recursive: false, - has_non_tail_self_call: false, - match_pattern: std::option::Option::None, - module_item_kind: item.module_item_kind.clone(), - expr_data: Rc::new(ExprData::NoExprData), - ident: None, + Rc::new(TypedItemResult { + item: Rc::new(Node { + occurrence_identity: item.occurrence_identity.clone(), + name: item.name.clone(), + span: item.span.clone(), + ident_span: item.ident_span.clone(), + children: Rc::new({ + let mut __result = Vec::new(); + for c in item.children.clone().iter().cloned() { + __result.push( + infer_item(c.clone(), scope.clone()).item.clone(), + ); + } + __result }), - diagnostics: v1_rt::concat( - transport_diags.clone(), - props_diags.clone(), - ), - }) - } + params: item.params.clone(), + inferred: if (item.inferred.clone() != std::option::Option::None) { + item.inferred.clone() + } else { + Some(Rc::new(InferredNode::Resolved { node: unit_type() })) + }, + return_cardinality: item.return_cardinality.clone(), + uses: item.uses.clone(), + body: std::option::Option::None, + connective: item.connective.clone(), + transport: typed_transport.clone(), + properties: typed_properties.clone(), + type_annotation: typed_anno.clone(), + is_self_recursive: false, + has_non_tail_self_call: false, + match_pattern: std::option::Option::None, + module_item_kind: item.module_item_kind.clone(), + expr_data: Rc::new(ExprData::NoExprData), + ident: None, + }), + diagnostics: v1_rt::concat( + transport_diags.clone(), + props_diags.clone(), + ), + }) } } } @@ -20949,7 +20870,6 @@ pub fn interface_cache_from_module(cache: Rc) -> Rc pub fn export_kind_of_item_kind(kind: ItemKind) -> Option { match kind.clone() { ItemKind::FnItem => Some(ExportKind::ExportFn), - ItemKind::FuncItem => Some(ExportKind::ExportFn), ItemKind::TypeItem => Some(ExportKind::ExportType), ItemKind::DataItem => Some(ExportKind::ExportData), ItemKind::ServiceItem => Some(ExportKind::ExportService), @@ -24303,23 +24223,6 @@ pub fn build_item_info( let item_name_str = crate::v1_std_core::authored_name_at(source_indices.clone(), item.clone()); match kind.clone() { - ItemKind::FuncItem => Rc::new(ItemInfo { - name: item_name_str.clone(), - module_name: module_name.clone(), - kind: kind.clone(), - service_names: if (item.body.clone() == std::option::Option::None) { - Rc::new(vec![]) - } else { - crate::v1_compiler_infer_service::collect_typed_service_calls( - item.body.clone().clone().unwrap(), - source_indices.clone(), - ) - }, - resource_names: res_names.clone(), - params: item.params.clone(), - is_self_recursive: false, - has_non_tail_self_call: false, - }), ItemKind::FnItem => Rc::new(ItemInfo { name: item_name_str.clone(), module_name: module_name.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_items.rs b/src/v1/stage0/src/v1_compiler_infer_items.rs index 13920de4e68..4c57842150b 100644 --- a/src/v1/stage0/src/v1_compiler_infer_items.rs +++ b/src/v1/stage0/src/v1_compiler_infer_items.rs @@ -51,7 +51,6 @@ use std::rc::Rc; #[serde(tag = "_variant")] pub enum ItemKind { FnItem, - FuncItem, TypeItem, DataItem, ServiceItem, @@ -245,25 +244,19 @@ pub fn item_kind(item: Rc) -> ItemKind { ItemKind::ServiceItem } else { if ((item.body.clone() != std::option::Option::None) - && ((item.uses.clone().len() as i64) > 0)) + && ((item.params.clone().len() as i64) > 0)) { - ItemKind::FuncItem + ItemKind::FnItem } else { if ((item.body.clone() != std::option::Option::None) - && ((item.params.clone().len() as i64) > 0)) + && (item.type_annotation.clone() != std::option::Option::None)) { - ItemKind::FnItem + ItemKind::DataItem } else { - if ((item.body.clone() != std::option::Option::None) - && (item.type_annotation.clone() != std::option::Option::None)) - { - ItemKind::DataItem + if (item.body.clone() != std::option::Option::None) { + ItemKind::FnItem } else { - if (item.body.clone() != std::option::Option::None) { - ItemKind::FnItem - } else { - ItemKind::OtherItem - } + ItemKind::OtherItem } } } @@ -313,8 +306,6 @@ pub fn leaf_owner_modules_from_registry( #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct FnItem; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct FuncItem; -#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct TypeItem; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct DataItem; diff --git a/src/v1/stage0/src/v1_compiler_infer_lookup.rs b/src/v1/stage0/src/v1_compiler_infer_lookup.rs index de41bddc130..765ed061a04 100644 --- a/src/v1/stage0/src/v1_compiler_infer_lookup.rs +++ b/src/v1/stage0/src/v1_compiler_infer_lookup.rs @@ -47,7 +47,7 @@ pub use crate::v1_compiler_infer_env::{ pub use crate::v1_compiler_infer_items::item_kind; pub use crate::v1_compiler_infer_items::ItemKind; use crate::v1_compiler_infer_items::ItemKind::{ - DataItem, FnItem, FuncItem, OtherItem, ServiceItem, TypeItem, + DataItem, FnItem, OtherItem, ServiceItem, TypeItem, }; pub use crate::v1_compiler_infer_method::infer_builtin_call_type; pub use crate::v1_compiler_infer_resolve::{fn_type_param_names, peel_nominal_alias_identity}; diff --git a/src/v1/stage0/src/v1_compiler_infer_service.rs b/src/v1/stage0/src/v1_compiler_infer_service.rs index c0666623a57..eef1de61c37 100644 --- a/src/v1/stage0/src/v1_compiler_infer_service.rs +++ b/src/v1/stage0/src/v1_compiler_infer_service.rs @@ -9,7 +9,7 @@ pub use crate::std_occurrence_identity::NodeOccurrenceIdentity; use crate::std_occurrence_identity::NodeOccurrenceIdentity::OccurrenceSynthetic; pub use crate::std_types::SourceSpan; pub use crate::v1_compiler_infer_items::inferred_to_outputs; -use crate::v1_compiler_infer_items::ItemKind::FuncItem; +use crate::v1_compiler_infer_items::ItemKind::*; pub use crate::v1_compiler_infer_items::{ItemInfo, ItemKind, TypedModule}; pub use crate::v1_compiler_infer_types::{emit_map_has, nominal_type_ref}; use crate::v1_rt; diff --git a/src/v1/stage0/src/v1_interpreter.rs b/src/v1/stage0/src/v1_interpreter.rs index a3ffc7242d7..69b6eb42cb6 100644 --- a/src/v1/stage0/src/v1_interpreter.rs +++ b/src/v1/stage0/src/v1_interpreter.rs @@ -6107,7 +6107,7 @@ fn eval_var( return Ok(v); } } - ItemKind::FuncItem | ItemKind::FnItem => { + ItemKind::FnItem => { return Ok(Value::Fn { node: fn_node.clone(), }); @@ -6140,7 +6140,7 @@ fn eval_var( return Ok(v); } } - ItemKind::FuncItem | ItemKind::FnItem => { + ItemKind::FnItem => { return Ok(Value::Fn { node: fn_node.clone(), }); @@ -7634,7 +7634,7 @@ fn eval_call(node: &Rc, env: &Rc, ctx: &InterpContext) -> InterpResul // // THE GATE IS THE BINDING, NOT THE VALUE'S REPRESENTATION. It formerly matched // `Value::Closure` only, so the law held for a LAMBDA and failed for a NAMED top-level - // function -- `Value::Fn` (the `ItemKind::FuncItem | FnItem` arm of `eval_expr`'s identifier + // function -- `Value::Fn` (the `ItemKind::FnItem` arm of `eval_expr`'s identifier // path), which fell past every tier and was answered by `ctx.lookup_fn` at the FREE FUNCTION // sharing its spelling. Measured on the 3x2 grid (let / parameter / pattern x named-fn / // lambda) in `v2.test.claim.local_binding_shadow`: the three named-fn cells reached the free diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index 5d987fc0bfa..fb246d808e0 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -763,6 +763,10 @@ pub enum CompilerDiagnostic { candidates: Rc>, span: Rc, }, + EffectfulSelfRecursionUnrealized { + name: String, + span: Rc, + }, ModuleFilenameCollision { filename: String, modules: Rc>, @@ -986,6 +990,7 @@ pub fn diagnostic_to_span(d: Rc) -> Rc { CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { span: s, .. } => s.clone(), CompilerDiagnostic::ParameterDefaultFormNotAdmitted { span: s, .. } => s.clone(), CompilerDiagnostic::AmbiguousAnonymousRecordLiteral { span: s, .. } => s.clone(), + CompilerDiagnostic::EffectfulSelfRecursionUnrealized { span: s, .. } => s.clone(), CompilerDiagnostic::ModuleFilenameCollision { span: s, .. } => s.clone(), CompilerDiagnostic::EffectSummaryIncompleteAtFunctionValue { span: s, .. } => s.clone(), CompilerDiagnostic::EffectSummaryIncompleteAtLocalBinding { span: s, .. } => s.clone(), @@ -1055,6 +1060,7 @@ pub fn diagnostic_to_message(d: Rc) -> String { CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { name: n, .. } => v1_rt::concat(v1_rt::concat("visible declarations of '".to_string(), n.clone()), "' could not be enumerated: the import re-export walk exceeded its depth bound, so no verdict is asserted about how many declarations answer to the name".to_string()), CompilerDiagnostic::ParameterDefaultFormNotAdmitted { parameter: p, admitted: forms, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("default value for parameter '".to_string(), p.clone()), "' is not an admitted form (admitted: ".to_string()), forms.clone().join(&", ".to_string())), ")".to_string()), CompilerDiagnostic::AmbiguousAnonymousRecordLiteral { candidates: cs, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("ambiguous anonymous record literal shape matches ".to_string(), ((cs.clone().len() as i64)).to_string()), " structs: ".to_string()), cs.clone().join(&", ".to_string())), " — add a nominal type".to_string()), + CompilerDiagnostic::EffectfulSelfRecursionUnrealized { name: n, .. } => v1_rt::concat(v1_rt::concat("effectful declaration '".to_string(), n.clone()), "' calls itself: the Rust realization renders an effectful declaration as `async fn`, and rustc refuses recursion in an async fn without boxing (E0733), which no emitter performs. Realize the recursion as a loop, or move the self-call into a pure helper.".to_string()), CompilerDiagnostic::ModuleFilenameCollision { filename: f, modules: ms, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("module filename collision: ".to_string(), ((ms.clone().len() as i64)).to_string()), " modules render one emitted file name '".to_string()), f.clone()), "': ".to_string()), ms.clone().join(&", ".to_string())), " — module_to_filename maps '.' to '_', so these names are indistinguishable at the emitted path; rename one module segment".to_string()), CompilerDiagnostic::EffectSummaryIncompleteAtFunctionValue { caller: c, .. } => v1_rt::concat(v1_rt::concat("effect summary incomplete: ".to_string(), c.clone()), " calls through a function value, whose callee is chosen at runtime, so its effects are unknown rather than empty — the caller's summary is a lower bound, not the answer".to_string()), CompilerDiagnostic::EffectSummaryIncompleteAtLocalBinding { caller: c, name: n, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("effect summary incomplete: ".to_string(), c.clone()), " calls the local binding '".to_string()), n.clone()), "', whose effects this pass cannot join through the registry, so the caller's summary is a lower bound rather than the answer".to_string()), @@ -1293,6 +1299,10 @@ pub fn diagnostic_disposition(d: Rc) -> Rc Rc::new(DiagnosticDisposition { severity: DiagnosticSeverity::SeverityError, gate: Rc::new(DiagnosticGateDisposition::GateBlocking), +}), + CompilerDiagnostic::EffectfulSelfRecursionUnrealized { .. } => Rc::new(DiagnosticDisposition { + severity: DiagnosticSeverity::SeverityError, + gate: Rc::new(DiagnosticGateDisposition::GateBlocking), }), CompilerDiagnostic::ModuleFilenameCollision { .. } => Rc::new(DiagnosticDisposition { severity: DiagnosticSeverity::SeverityError, diff --git a/src/v2/lens/interface_summary.dag b/src/v2/lens/interface_summary.dag index 89f249cdbc7..e49f849e450 100644 --- a/src/v2/lens/interface_summary.dag +++ b/src/v2/lens/interface_summary.dag @@ -4,7 +4,6 @@ module v2.lens.interface_summary fn decl_kind_to_export_kind(kind: ItemKind) -> ExportKind? { match kind { FnItem => Present { value: ExportFn } - FuncItem => Present { value: ExportFn } TypeItem => Present { value: ExportType } DataItem => Present { value: ExportData } ServiceItem => Present { value: ExportService } diff --git a/src/v2/std/decl_index.dag b/src/v2/std/decl_index.dag index 802d07b62b3..62fd8991ab6 100644 --- a/src/v2/std/decl_index.dag +++ b/src/v2/std/decl_index.dag @@ -1,7 +1,6 @@ module v2.std.decl_index type ItemKind = FnItem - | FuncItem | TypeItem | DataItem | ServiceItem @@ -71,7 +70,6 @@ fn logical_qualified_name_from_module(module_name: String, name: String) -> Stri fn decl_facts_is_fn_like(kind: ItemKind) -> Bool { match kind { FnItem => true - FuncItem => true _ => false } }