From 576bafc9eec67d9951d9fdd2cad1164b08bc47c7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 05:25:52 +0000 Subject: [PATCH 01/24] Text crossings unfold or refuse: exact-representation text compat (XL-0T ruling B) Kernel String is host text and a corpus-declared FreeMonoid is a code-point sequence. One classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation), is now read by the corpus compatibility relation, call arguments, declared returns and data initializers, builtin arguments (previously admitted untyped) and the Rust renderer, so a pairing the checker admits is one the emitter realizes with one carrier. - A host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node (scalar-string `chars`, typed as the destination), gated on the existing literal_homomorphism_rows UnicodeScalarSequenceUnfold row. Every other crossing refuses, located. - Call arguments are typed against the declaration-bound formal, not the parameter spelling re-resolved in the caller: the caller-re-resolution escape of gunbc.rung_drop text_boundary_identity_wall is closed. - 71 modules imported std.string_type { String } while treating the values as host text (a nickname); the imports are deleted. std.string_type keeps its two functions over the kernel String and loses its structural alias. - A kernel spelling no longer resolves through the global bare fallback, which the deleted duplicate had been masking. - string_eq over two host operands becomes == in five src/v2/lens modules; jq's host string_join becomes concat. Evidence: test.claim.text_boundary_identity_wall_witness_test (16 rows) and the flipped restoration probe in self_host_structural_text_witness_test. Whole-corpus compile, base seed vs head seed over the same tree: 0 new blocking rows, 4 removed. The drop stays Standing, narrowed to callable values (eq: string_eq) with a restated trigger. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../posix_effective_principal_read_op.dag | 1 - dag/extdeps/bmc/http.dag | 1 - dag/extdeps/bmc/ipmi.dag | 1 - dag/extdeps/bmc/megarac.dag | 1 - dag/extdeps/bmc/openbmc_fan_control.dag | 1 - .../bmc/openbmc_password_ssh_transport.dag | 1 - dag/extdeps/clock/clock.dag | 1 - dag/extdeps/cloud/gcp/gcp.dag | 1 - dag/extdeps/cloudflare/account_api_tokens.dag | 12 +- dag/extdeps/cron/cron.dag | 1 - dag/extdeps/crypto/hash.dag | 1 - dag/extdeps/docker/cgroup.dag | 1 - dag/extdeps/docker/container_inspect.dag | 1 - dag/extdeps/docker/container_stats.dag | 1 - dag/extdeps/entropy/entropy.dag | 1 - dag/extdeps/git/git.dag | 1 - dag/extdeps/git/inspect.dag | 1 - dag/extdeps/git/publication_transport.dag | 1 - dag/extdeps/github/issues.dag | 1 - dag/extdeps/github/pulls.dag | 1 - dag/extdeps/gunbc/gunbc.dag | 1 - dag/extdeps/http/client.dag | 1 - dag/extdeps/linux/cgroup_v2.dag | 1 - dag/extdeps/linux/edac.dag | 1 - dag/extdeps/linux/proc_net_tcp.dag | 1 - dag/extdeps/linux/proc_pid_stat.dag | 1 - dag/extdeps/linux/proc_stat.dag | 1 - dag/extdeps/linux/procfs.dag | 1 - dag/extdeps/llm/cli.dag | 1 - dag/extdeps/llm/codex_app_server.dag | 1 - .../nvidia/system_management_interface.dag | 1 - dag/extdeps/package_managers/apt.dag | 1 - dag/extdeps/posix/getconf.dag | 1 - dag/extdeps/procps/pgrep.dag | 1 - dag/extdeps/python/python.dag | 1 - dag/extdeps/rust/cargo_build.dag | 1 - dag/extdeps/rust/rustc.dag | 1 - dag/extdeps/shell.dag | 1 - dag/extdeps/shell/exec.dag | 1 - dag/extdeps/ssh/password_session.dag | 1 - dag/extdeps/ssh/session.dag | 1 - .../sudo/nopasswd_execute_probe_check_op.dag | 1 - dag/extdeps/systemd/oomd.dag | 1 - dag/extdeps/systemd/systemctl.dag | 1 - dag/extdeps/systemd/systemd_run.dag | 1 - dag/extdeps/tailscale/serve.dag | 1 - dag/extdeps/tmux/tmux.dag | 1 - dag/extdeps/tools/coreutils_stat.dag | 1 - dag/extdeps/tools/diffutils.dag | 1 - dag/extdeps/tools/hostname.dag | 1 - dag/extdeps/tools/id.dag | 1 - dag/extdeps/tools/jq.dag | 6 +- dag/extdeps/tools/node.dag | 1 - dag/extdeps/tools/npm.dag | 1 - dag/extdeps/tools/rustfmt.dag | 1 - dag/extdeps/tools/sed.dag | 1 - dag/extdeps/tools/sha256sum.dag | 1 - dag/extdeps/tools/sha512sum.dag | 1 - dag/extdeps/tools/sleep.dag | 1 - dag/extdeps/tools/stat.dag | 1 - dag/extdeps/tools/xorriso.dag | 1 - dag/gunbc/cli_services.dag | 1 - dag/gunbc/fleet_observation/capture_chunk.dag | 1 - .../fleet_observation/collector_ownership.dag | 1 - .../collector_supervision.dag | 1 - .../observation_envelope.dag | 1 - dag/gunbc/machine_intake/sol_hold.dag | 1 - ...epted_source_emits_uncompilable_target.dag | 1 + .../rung_drop/text_boundary_identity_wall.dag | 2 +- dag/std/coercion.dag | 9 + dag/std/markup.dag | 1 - dag/std/string_type.dag | 12 +- ...fleet_observation_capture_witness_test.dag | 1 - .../mtcollins1_census_image_witness_test.dag | 1 - ...self_host_structural_text_witness_test.dag | 36 +- ...t_symbol_identity_binding_witness_test.dag | 6 +- ...xt_boundary_identity_wall_witness_test.dag | 151 +++++ src/v1/04_env.dag | 12 + src/v1/04_infer.dag | 242 ++++++- src/v1/04_method.dag | 31 +- src/v1/04_types.dag | 39 +- src/v1/05_emit_rust.dag | 42 +- src/v1/coercion.dag | 94 ++- src/v1/stage0/src/std_coercion.rs | 17 + src/v1/stage0/src/v1_compiler_coercion.rs | 162 ++++- src/v1/stage0/src/v1_compiler_emit_rust.rs | 58 +- src/v1/stage0/src/v1_compiler_infer.rs | 524 ++++++++++++--- src/v1/stage0/src/v1_compiler_infer_env.rs | 8 + src/v1/stage0/src/v1_compiler_infer_method.rs | 62 +- src/v1/stage0/src/v1_compiler_infer_types.rs | 604 +++++++++--------- src/v2/compiler/discovery_enumeration.dag | 1 - src/v2/lens/effect_reach.dag | 22 +- src/v2/lens/enforcement/grammar_coverage.dag | 2 +- src/v2/lens/live_read_classification.dag | 20 +- src/v2/lens/module_graph.dag | 4 +- src/v2/lens/reference_deps.dag | 6 +- .../workflow/floor_grandfathered_roster.dag | 2 +- 97 files changed, 1614 insertions(+), 641 deletions(-) create mode 100644 dag/test/claim/text_boundary_identity_wall_witness_test.dag diff --git a/dag/extdeps/access/posix_effective_principal_read_op.dag b/dag/extdeps/access/posix_effective_principal_read_op.dag index 4389b57ce85..fa49285d068 100644 --- a/dag/extdeps/access/posix_effective_principal_read_op.dag +++ b/dag/extdeps/access/posix_effective_principal_read_op.dag @@ -1,7 +1,6 @@ module extdeps.access.posix_effective_principal_read_op import std.types { Int, Bool, List, Unit } -import std.string_type { String } import extdeps.access.posix_effective_principal { EffectivePosixPrincipalRead, EffectivePosixPrincipalReadOutcome, } diff --git a/dag/extdeps/bmc/http.dag b/dag/extdeps/bmc/http.dag index 8676e99a3d3..95613ede0ba 100644 --- a/dag/extdeps/bmc/http.dag +++ b/dag/extdeps/bmc/http.dag @@ -5,7 +5,6 @@ import extdeps.uri { Uri, Https } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.disposition { Disposition, RealizationDispatch, Scaffold } import std.types { Bool, Int, NonEmptyStr, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { scheme: Https diff --git a/dag/extdeps/bmc/ipmi.dag b/dag/extdeps/bmc/ipmi.dag index 552a89be6ab..1cdaa1cfb6c 100644 --- a/dag/extdeps/bmc/ipmi.dag +++ b/dag/extdeps/bmc/ipmi.dag @@ -3,7 +3,6 @@ module extdeps.bmc.ipmi import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import std.types { Bool, Int, NonEmptyStr, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { scheme: Https diff --git a/dag/extdeps/bmc/megarac.dag b/dag/extdeps/bmc/megarac.dag index 5328c19bc1f..0816f05dead 100644 --- a/dag/extdeps/bmc/megarac.dag +++ b/dag/extdeps/bmc/megarac.dag @@ -2,7 +2,6 @@ module extdeps.bmc.megarac import std.decl_ref { DeclarationRef, WholeDeclaration } import std.types { Bool, Int, NonEmptyStr, List, Unit } -import std.string_type { String } import std.credentials { FactoryLogin } import extdeps.bmc.endpoint { AmiMegaRac, BmcProtocol, Ipmi } import extdeps.bmc.capability { diff --git a/dag/extdeps/bmc/openbmc_fan_control.dag b/dag/extdeps/bmc/openbmc_fan_control.dag index 29f1277fb92..5210488b888 100644 --- a/dag/extdeps/bmc/openbmc_fan_control.dag +++ b/dag/extdeps/bmc/openbmc_fan_control.dag @@ -137,7 +137,6 @@ import std.measure { second_count, } import std.types { Bool, EpochMs, Int, List, NonEmptyStr } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/bmc/openbmc_password_ssh_transport.dag b/dag/extdeps/bmc/openbmc_password_ssh_transport.dag index d2b675e38fb..ecca7b33ba6 100644 --- a/dag/extdeps/bmc/openbmc_password_ssh_transport.dag +++ b/dag/extdeps/bmc/openbmc_password_ssh_transport.dag @@ -4,7 +4,6 @@ import extdeps.exec.command { sshpass_authority, sshpass_binary_name } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Https, Uri } import std.types { Bool, Int, NonEmptyStr, Secret } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/clock/clock.dag b/dag/extdeps/clock/clock.dag index 1700a067d40..ba0da3cbbfb 100644 --- a/dag/extdeps/clock/clock.dag +++ b/dag/extdeps/clock/clock.dag @@ -4,7 +4,6 @@ import std.algebra { trim } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import std.types { EpochMs, EpochSecs, Timestamp, Unit } -import std.string_type { String } import std.nat { Nat } import v2.std.optional { Present } diff --git a/dag/extdeps/cloud/gcp/gcp.dag b/dag/extdeps/cloud/gcp/gcp.dag index c88e75ab968..2930659662c 100644 --- a/dag/extdeps/cloud/gcp/gcp.dag +++ b/dag/extdeps/cloud/gcp/gcp.dag @@ -4,7 +4,6 @@ module extdeps.cloud.gcp.gcp import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import std.types { Bool, FilePath, Int, List, Map, NonEmptyStr, Secret, Unit } -import std.string_type { String } import v2.std.algebra { non_empty } import extdeps.ietf.http_semantics { POST } diff --git a/dag/extdeps/cloudflare/account_api_tokens.dag b/dag/extdeps/cloudflare/account_api_tokens.dag index 808d2e7d49d..3d50b91d27a 100644 --- a/dag/extdeps/cloudflare/account_api_tokens.dag +++ b/dag/extdeps/cloudflare/account_api_tokens.dag @@ -1,13 +1,11 @@ module extdeps.cloudflare.account_api_tokens -// THE DECLARING MODULE IS NAMED, NOT A MODULE THAT MERELY MENTIONS THE SPELLING. std.types does not -// declare String -- std.string_type does (type String = FreeMonoid) -- so importing it from -// std.types bound nothing and left `String` here as an ambiguous bare read between std.string_type -// and v2.std.text. The floor's bare-name wall says exactly that, and warns that copying an adjacent -// file's import line is the usual way to get it wrong; the 41 sibling extdeps modules that name -// std.string_type are the convention this now follows. +// `String` HERE IS THE KERNEL STRING, UNIMPORTED, BECAUSE THE VALUES ARE HOST TEXT. This module +// used to import std.string_type { String } (type String = FreeMonoid, a code-point +// sequence) and then treat every such value as host text, which is a crossing between the two +// text representations with no unfold (gunbc.rung_drop text_boundary_identity_wall). The kernel +// spelling needs no import: it is in kernel_type_set and imports never override it. import std.types { NonEmptyStr, Secret, Timestamp, List, Map, Bool } -import std.string_type { String } import std.decl_ref { DeclarationRef, WholeDeclaration, decl_ref } import std.roster_frontier { FrontierRow, frontier_row_decl, frontier_row_path } import std.dissolution { unbound_dissolution } diff --git a/dag/extdeps/cron/cron.dag b/dag/extdeps/cron/cron.dag index 3d8952f92fd..5f600fbb6dc 100644 --- a/dag/extdeps/cron/cron.dag +++ b/dag/extdeps/cron/cron.dag @@ -4,7 +4,6 @@ module extdeps.cron import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import std.types { Bool, List } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { scheme: Https diff --git a/dag/extdeps/crypto/hash.dag b/dag/extdeps/crypto/hash.dag index 8b3a6c9b178..7db22df0287 100644 --- a/dag/extdeps/crypto/hash.dag +++ b/dag/extdeps/crypto/hash.dag @@ -1,7 +1,6 @@ module extdeps.crypto.hash import std.types { NonEmptyStr, Bool, List, Unit } -import std.string_type { String } import std.algebra { trim } import v2.std.algebra { skip } import std.content_hash { diff --git a/dag/extdeps/docker/cgroup.dag b/dag/extdeps/docker/cgroup.dag index 0c957dd6e18..58bdb68711a 100644 --- a/dag/extdeps/docker/cgroup.dag +++ b/dag/extdeps/docker/cgroup.dag @@ -1,7 +1,6 @@ module extdeps.docker.cgroup import std.types { NonEmptyStr, Bool, Int, List } -import std.string_type { String } import std.content_hash { content_hash_validate_lower_hex_syntax, content_hash_validate_lower_hex_length } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/docker/container_inspect.dag b/dag/extdeps/docker/container_inspect.dag index 29fdb847885..4ec618d5d49 100644 --- a/dag/extdeps/docker/container_inspect.dag +++ b/dag/extdeps/docker/container_inspect.dag @@ -1,6 +1,5 @@ module extdeps.docker.container_inspect -import std.string_type { String } import std.types { Timestamp, Int, FilePath, NonEmptyStr } import std.nat { Nat } import std.measure { ByteSize, byte_size, byte_size_count, Microsecond, microsecond, microsecond_count } diff --git a/dag/extdeps/docker/container_stats.dag b/dag/extdeps/docker/container_stats.dag index 63168277958..51b598bf7d7 100644 --- a/dag/extdeps/docker/container_stats.dag +++ b/dag/extdeps/docker/container_stats.dag @@ -1,6 +1,5 @@ module extdeps.docker.container_stats -import std.string_type { String } import std.types { Timestamp, Int, Float } import std.nat { Nat } import std.measure { ByteSize, byte_size, byte_size_count, Nanosecond, nanosecond, nanosecond_count, Microsecond } diff --git a/dag/extdeps/entropy/entropy.dag b/dag/extdeps/entropy/entropy.dag index 4c44c2eddd4..3d569e3ed5b 100644 --- a/dag/extdeps/entropy/entropy.dag +++ b/dag/extdeps/entropy/entropy.dag @@ -7,7 +7,6 @@ import std.decl_ref { DeclarationRef, WholeDeclaration } import std.disposition { Disposition, RealizationDispatch, Scaffold, SingleAuthority } import std.dissolution { DissolutionCondition, unbound_dissolution } import std.types { Int, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { scheme: Https diff --git a/dag/extdeps/git/git.dag b/dag/extdeps/git/git.dag index ff8c99778b4..509f5e7fe1d 100644 --- a/dag/extdeps/git/git.dag +++ b/dag/extdeps/git/git.dag @@ -5,7 +5,6 @@ import extdeps.uri { Uri, Https } import extdeps.exec.command { ArgvCommand, argv_command, shell_quote } import std.measure { Percent, percent } import std.types { Bool, CommitSha, Email, FilePath, GitRef, Int, List, NonEmptyStr, Seconds, Timestamp, Unit } -import std.string_type { String } import v2.std.algebra { length, skip } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/git/inspect.dag b/dag/extdeps/git/inspect.dag index ed3684f2c59..07c87829847 100644 --- a/dag/extdeps/git/inspect.dag +++ b/dag/extdeps/git/inspect.dag @@ -1,7 +1,6 @@ module extdeps.git.inspect import std.types { Bool, CommitSha, FilePath, GitRef, Int, Unit } -import std.string_type { String } import std.algebra { trim } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/git/publication_transport.dag b/dag/extdeps/git/publication_transport.dag index 3d12a9c0f60..689e3bec525 100644 --- a/dag/extdeps/git/publication_transport.dag +++ b/dag/extdeps/git/publication_transport.dag @@ -1,7 +1,6 @@ module extdeps.git.publication_transport import std.types { Bool, CommitSha, GitRef, Int, List, NonEmptyStr, Unit } -import std.string_type { String } import extdeps.git.object_store { GitObjectId, GitRefName, git_object_id_wire_hex } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } diff --git a/dag/extdeps/github/issues.dag b/dag/extdeps/github/issues.dag index 45c9f313457..d7624a23427 100644 --- a/dag/extdeps/github/issues.dag +++ b/dag/extdeps/github/issues.dag @@ -2,7 +2,6 @@ module extdeps.github.issues import extdeps.github.github { GitHubUser, default_api_base, default_per_page } import extdeps.github.errors { GitHubErrorShape } -import std.string_type { String } import std.types { NonEmptyStr, Timestamp, Secret } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/github/pulls.dag b/dag/extdeps/github/pulls.dag index d18fa0062be..d57085f5356 100644 --- a/dag/extdeps/github/pulls.dag +++ b/dag/extdeps/github/pulls.dag @@ -2,7 +2,6 @@ module extdeps.github.pulls import extdeps.github.github { GitHubUser, default_api_base, default_per_page } import std.types { CommitSha, EpochMs, FilePath, GitRef, NonEmptyStr, Timestamp, AuthScheme } -import std.string_type { String } import extdeps.github.errors { GitHubErrorShape } import std.credentials { CredentialSource, EnvVar } import std.serialization { WireFormat, Text } diff --git a/dag/extdeps/gunbc/gunbc.dag b/dag/extdeps/gunbc/gunbc.dag index bef7fd9f9f9..cebd2e34876 100644 --- a/dag/extdeps/gunbc/gunbc.dag +++ b/dag/extdeps/gunbc/gunbc.dag @@ -1,7 +1,6 @@ module extdeps.gunbc import std.types { FilePath, Unit } -import std.string_type { String } import std.shell_stream_capture { WitnessStderrCapturePolicy, BoundedTail } import std.measure { byte_size } import extdeps.external_authority { ExternalAuthority } diff --git a/dag/extdeps/http/client.dag b/dag/extdeps/http/client.dag index 43596819376..64e40dac433 100644 --- a/dag/extdeps/http/client.dag +++ b/dag/extdeps/http/client.dag @@ -1,7 +1,6 @@ module extdeps.http.client import std.types { NonEmptyStr, Bool, List, Unit, Int } -import std.string_type { String } import std.measure { Second, second_count } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/linux/cgroup_v2.dag b/dag/extdeps/linux/cgroup_v2.dag index d1a878ef903..16980c6ecd8 100644 --- a/dag/extdeps/linux/cgroup_v2.dag +++ b/dag/extdeps/linux/cgroup_v2.dag @@ -1,7 +1,6 @@ module extdeps.linux.cgroup_v2 import std.types { Bool, NonEmptyStr, Unit, Int, List } -import std.string_type { String } import std.algebra { trim } import std.os.types { KernelFamily, Linux, Darwin, WindowsNt, LinuxGuestOnWindows } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } diff --git a/dag/extdeps/linux/edac.dag b/dag/extdeps/linux/edac.dag index 6de9a125d3d..3d644b905f1 100644 --- a/dag/extdeps/linux/edac.dag +++ b/dag/extdeps/linux/edac.dag @@ -7,7 +7,6 @@ import extdeps.uri { Uri, Https } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.measure { MemoryControllerCount, memory_controller_count, memory_controller_count_value } import v2.std.optional { Absent, Present } -import std.string_type { String } import std.types { Bool, NonEmptyStr, Unit } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/linux/proc_net_tcp.dag b/dag/extdeps/linux/proc_net_tcp.dag index 76ffefa17d4..779e4ca2888 100644 --- a/dag/extdeps/linux/proc_net_tcp.dag +++ b/dag/extdeps/linux/proc_net_tcp.dag @@ -1,7 +1,6 @@ module extdeps.linux.proc_net_tcp import std.types { NonEmptyStr, List, Bool, Int, Port } -import std.string_type { String } import v2.std.optional { Present, Absent } import std.algebra { trim } import v2.std.algebra { any } diff --git a/dag/extdeps/linux/proc_pid_stat.dag b/dag/extdeps/linux/proc_pid_stat.dag index bcb5e0ab8bd..b2011ac305b 100644 --- a/dag/extdeps/linux/proc_pid_stat.dag +++ b/dag/extdeps/linux/proc_pid_stat.dag @@ -2,7 +2,6 @@ module extdeps.linux.proc_pid_stat import std.types { Int, List, NonEmptyStr, EpochSecs } import std.measure { Hertz, hertz_count } -import std.string_type { String } import std.algebra { trim } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } diff --git a/dag/extdeps/linux/proc_stat.dag b/dag/extdeps/linux/proc_stat.dag index 432dab79705..f000888a313 100644 --- a/dag/extdeps/linux/proc_stat.dag +++ b/dag/extdeps/linux/proc_stat.dag @@ -1,7 +1,6 @@ module extdeps.linux.proc_stat import std.types { Int, List, NonEmptyStr, EpochSecs } -import std.string_type { String } import std.algebra { trim } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } diff --git a/dag/extdeps/linux/procfs.dag b/dag/extdeps/linux/procfs.dag index 943c4a01beb..4475c39a33f 100644 --- a/dag/extdeps/linux/procfs.dag +++ b/dag/extdeps/linux/procfs.dag @@ -1,7 +1,6 @@ module extdeps.linux.procfs import std.types { NonEmptyStr, List, Bool, Unit } -import std.string_type { String } import std.os.types { KernelFamily, Linux, Darwin, WindowsNt, LinuxGuestOnWindows } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/llm/cli.dag b/dag/extdeps/llm/cli.dag index 5a2ed7b8573..77fc1d9657c 100644 --- a/dag/extdeps/llm/cli.dag +++ b/dag/extdeps/llm/cli.dag @@ -4,7 +4,6 @@ import extdeps.tools.env { env_path_resolved_program } import std.algebra { trim } import std.types { List, Int, Bool, NonEmptyStr, RenderedTerminalText, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.languages.json.grammar { diff --git a/dag/extdeps/llm/codex_app_server.dag b/dag/extdeps/llm/codex_app_server.dag index 5d832b446c8..59d8f54e841 100644 --- a/dag/extdeps/llm/codex_app_server.dag +++ b/dag/extdeps/llm/codex_app_server.dag @@ -1,7 +1,6 @@ module extdeps.llm.codex_app_server import std.types { NonEmptyStr, FilePath, Int, Bool, Unit } -import std.string_type { String } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/nvidia/system_management_interface.dag b/dag/extdeps/nvidia/system_management_interface.dag index 7a79e0a2299..7d82c8fc8a8 100644 --- a/dag/extdeps/nvidia/system_management_interface.dag +++ b/dag/extdeps/nvidia/system_management_interface.dag @@ -1,7 +1,6 @@ module extdeps.nvidia.system_management_interface import std.types { NonEmptyStr, Bool, Int, List, Unit } -import std.string_type { String } import std.nat { Nat } import std.measure { Mebibyte, mebibyte } import std.algebra { trim } diff --git a/dag/extdeps/package_managers/apt.dag b/dag/extdeps/package_managers/apt.dag index 2801beb0c27..cac854620e1 100644 --- a/dag/extdeps/package_managers/apt.dag +++ b/dag/extdeps/package_managers/apt.dag @@ -1,7 +1,6 @@ module extdeps.apt import std.types { NonEmptyStr, FilePath, Bool, List, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/posix/getconf.dag b/dag/extdeps/posix/getconf.dag index cdf27bb3829..2df22894ad2 100644 --- a/dag/extdeps/posix/getconf.dag +++ b/dag/extdeps/posix/getconf.dag @@ -1,7 +1,6 @@ module extdeps.posix.getconf import std.types { Bool, Int, Unit } -import std.string_type { String } import std.algebra { trim } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/procps/pgrep.dag b/dag/extdeps/procps/pgrep.dag index fef87078d9e..a4709026f2c 100644 --- a/dag/extdeps/procps/pgrep.dag +++ b/dag/extdeps/procps/pgrep.dag @@ -1,7 +1,6 @@ module extdeps.procps.pgrep import std.types { Int, List, Bool, NonEmptyStr, Unit } -import std.string_type { String } import std.algebra { trim } // pgrep(1) from procps-ng, cited to the upstream manual's EXIT STATUS section. Only the codes this diff --git a/dag/extdeps/python/python.dag b/dag/extdeps/python/python.dag index cc02b089983..5e8585c7e4e 100644 --- a/dag/extdeps/python/python.dag +++ b/dag/extdeps/python/python.dag @@ -1,7 +1,6 @@ module extdeps.python import std.types { FilePath, List, Unit, NonEmptyStr } -import std.string_type { String } import std.workspace_artifact { WorkspaceFootprint, WorkspaceArtifact, CitedUpstream, RegenerableFromSource } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/rust/cargo_build.dag b/dag/extdeps/rust/cargo_build.dag index d5ec01281f8..d5d51e6b23b 100644 --- a/dag/extdeps/rust/cargo_build.dag +++ b/dag/extdeps/rust/cargo_build.dag @@ -1,7 +1,6 @@ module extdeps.cargo_build import std.types { Bool, FilePath, NonEmptyStr, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.exec.command { ArgvCommand, argv_command } diff --git a/dag/extdeps/rust/rustc.dag b/dag/extdeps/rust/rustc.dag index 0d8ec741ca5..dd6e2a1c4a1 100644 --- a/dag/extdeps/rust/rustc.dag +++ b/dag/extdeps/rust/rustc.dag @@ -1,7 +1,6 @@ module extdeps.rustc import std.types { Bool, Int, Unit } -import std.string_type { String } import std.algebra { trim } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/shell.dag b/dag/extdeps/shell.dag index e9bae0d17fb..c536422a90c 100644 --- a/dag/extdeps/shell.dag +++ b/dag/extdeps/shell.dag @@ -1,7 +1,6 @@ module extdeps.shell import std.types { FilePath, NonEmptyStr, Secret, List, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import std.dissolution { DissolutionCondition, unbound_dissolution } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/shell/exec.dag b/dag/extdeps/shell/exec.dag index 45e872da684..e20ee395d04 100644 --- a/dag/extdeps/shell/exec.dag +++ b/dag/extdeps/shell/exec.dag @@ -1,7 +1,6 @@ module extdeps.shell.exec import std.types { Int, Bool, NonEmptyStr, Unit } -import std.string_type { String } import v2.std.compilers.cli_surface { ProcessArgvExpansion } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/ssh/password_session.dag b/dag/extdeps/ssh/password_session.dag index 61644394f01..3dc8551a986 100644 --- a/dag/extdeps/ssh/password_session.dag +++ b/dag/extdeps/ssh/password_session.dag @@ -4,7 +4,6 @@ import extdeps.external_authority { ExternalAuthority } import extdeps.exec.command { sshpass_authority, sshpass_binary_name } import extdeps.uri { Uri, Https } import std.types { Bool, Int, List, NonEmptyStr, Secret, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/ssh/session.dag b/dag/extdeps/ssh/session.dag index 5bf23a7981c..a0b6964cd66 100644 --- a/dag/extdeps/ssh/session.dag +++ b/dag/extdeps/ssh/session.dag @@ -3,7 +3,6 @@ module extdeps.ssh.session import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import std.types { Bool, FilePath, Int, List, NonEmptyStr, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { scheme: Https diff --git a/dag/extdeps/sudo/nopasswd_execute_probe_check_op.dag b/dag/extdeps/sudo/nopasswd_execute_probe_check_op.dag index 4503d0ae1cc..bb2d832a3c1 100644 --- a/dag/extdeps/sudo/nopasswd_execute_probe_check_op.dag +++ b/dag/extdeps/sudo/nopasswd_execute_probe_check_op.dag @@ -1,7 +1,6 @@ module extdeps.sudo.nopasswd_execute_probe_check_op import std.types { NonEmptyStr, Bool, Int, List, Unit } -import std.string_type { String } import extdeps.sudo.nopasswd_execute_probe { SudoNopasswdExecuteProbeShape, SudoNopasswdGrantListProbeShape, diff --git a/dag/extdeps/systemd/oomd.dag b/dag/extdeps/systemd/oomd.dag index 4573ef54444..5240c58b01a 100644 --- a/dag/extdeps/systemd/oomd.dag +++ b/dag/extdeps/systemd/oomd.dag @@ -3,7 +3,6 @@ module extdeps.systemd.oomd import std.algebra { trim } import std.types { NonEmptyStr, FilePath, Bool, Unit } -import std.string_type { String } import std.measure { Second, second, Percent, percent, percent_count } import std.types { Int } diff --git a/dag/extdeps/systemd/systemctl.dag b/dag/extdeps/systemd/systemctl.dag index ab0b31b25ed..1fa7af1d4b4 100644 --- a/dag/extdeps/systemd/systemctl.dag +++ b/dag/extdeps/systemd/systemctl.dag @@ -2,7 +2,6 @@ module extdeps.systemd.systemctl import extdeps.transports.shell { ShellOutcome } import std.types { NonEmptyStr, List, Bool, Int, Unit } -import std.string_type { String } import std.algebra { trim } import extdeps.systemd { SystemdUnitProperty, systemd_unit_property_wire, systemd_binary_path } diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 732180a4ea2..964c3cc6c30 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -4,7 +4,6 @@ import std.types { NonEmptyStr, List, Bool, Int, Unit } import std.nat { Nat } import std.measure { ByteSize, byte_size } import std.algebra { trim } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/tailscale/serve.dag b/dag/extdeps/tailscale/serve.dag index 7c53b1f49f9..d8d1102e9ee 100644 --- a/dag/extdeps/tailscale/serve.dag +++ b/dag/extdeps/tailscale/serve.dag @@ -1,7 +1,6 @@ module extdeps.tailscale.serve import std.types { NonEmptyStr, Port, List, Bool, Unit } -import std.string_type { String } import std.key_relation { KeyRelation, KeyedResourceRelation, diff --git a/dag/extdeps/tmux/tmux.dag b/dag/extdeps/tmux/tmux.dag index 151e0ddbab3..6f2365cc3a3 100644 --- a/dag/extdeps/tmux/tmux.dag +++ b/dag/extdeps/tmux/tmux.dag @@ -3,7 +3,6 @@ module extdeps.tmux import std.algebra { trim } import std.types { List, NonEmptyStr, Int, Bool, RenderedTerminalText, Unit } -import std.string_type { String } import std.workspace_artifact { WorkspaceFootprint, WorkspaceArtifact, CitedUpstream, SessionRuntimeState } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/tools/coreutils_stat.dag b/dag/extdeps/tools/coreutils_stat.dag index 6bc7566410d..a8f21c9773f 100644 --- a/dag/extdeps/tools/coreutils_stat.dag +++ b/dag/extdeps/tools/coreutils_stat.dag @@ -4,7 +4,6 @@ import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.access.posix { FileOwnership, FileMode, file_mode_of_octal_text } import std.types { Bool, Int, List, NonEmptyStr, Unit } -import std.string_type { String } import std.algebra { trim } // OBSERVING WHO OWNS A PATH, as a modeled operation. extdeps.access.posix owns the CONCEPT -- diff --git a/dag/extdeps/tools/diffutils.dag b/dag/extdeps/tools/diffutils.dag index 8eb1308502b..2d5c756493b 100644 --- a/dag/extdeps/tools/diffutils.dag +++ b/dag/extdeps/tools/diffutils.dag @@ -2,7 +2,6 @@ module extdeps.tools.diffutils import std.types { FilePath, NonEmptyStr, Unit, Bool } import std.integer { Int } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/tools/hostname.dag b/dag/extdeps/tools/hostname.dag index ad7895e7367..0949c23972f 100644 --- a/dag/extdeps/tools/hostname.dag +++ b/dag/extdeps/tools/hostname.dag @@ -1,7 +1,6 @@ module extdeps.tools.hostname import std.types { List, Bool, Int, NonEmptyStr, Unit } -import std.string_type { String } import std.algebra { Empty, list_append, list_snoc_item } import v2.std.algebra { fold_list } import v2.std.collection { Map, empty_map, map_insert } diff --git a/dag/extdeps/tools/id.dag b/dag/extdeps/tools/id.dag index c92fb059a9e..3105227ef48 100644 --- a/dag/extdeps/tools/id.dag +++ b/dag/extdeps/tools/id.dag @@ -1,7 +1,6 @@ module extdeps.tools.id import std.types { NonEmptyStr, List, Bool, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.exec.command { ArgvCommand, argv_command } diff --git a/dag/extdeps/tools/jq.dag b/dag/extdeps/tools/jq.dag index 24256e9829f..429067b505b 100644 --- a/dag/extdeps/tools/jq.dag +++ b/dag/extdeps/tools/jq.dag @@ -2,11 +2,9 @@ module extdeps.tools.jq import std.occurrence_identity { OccurrenceSynthetic } import std.types { NonEmptyStr, Bool, Int, List, Unit } -import std.string_type { String } import std.algebra { Cons, Empty, trim, list_append, list_snoc_item } import v2.std.algebra { fold_list } import v2.std.collection { Map, empty_map, map_insert, map_lookup } -import v2.std.text { string_join } import v2.std.compilers.cli_surface { CliArgumentSyntax, CliSurface, cli_long_option_spelling, process_argv_expansion, serialize_cli_arguments } import v2.std.compilers.lexing { LexRuleSet, LiteralPattern, ModeledLexRules, TokenRule, symbol_intern_lexeme } import v2.std.compilers.target_model { BoundToken, FixedToken } @@ -283,11 +281,11 @@ data jq_binding_name_key_prefix: String = "gunbc.jq.binding.name." data jq_binding_value_key_prefix: String = "gunbc.jq.binding.value." fn jq_binding_name_key(binding_name: String) -> Symbol { - symbol_intern_lexeme(lexeme: string_join(fields: [jq_binding_name_key_prefix, binding_name], separator: "")) + symbol_intern_lexeme(lexeme: concat(jq_binding_name_key_prefix, binding_name)) } fn jq_binding_value_key(binding_name: String) -> Symbol { - symbol_intern_lexeme(lexeme: string_join(fields: [jq_binding_value_key_prefix, binding_name], separator: "")) + symbol_intern_lexeme(lexeme: concat(jq_binding_value_key_prefix, binding_name)) } type JqBindingsResult diff --git a/dag/extdeps/tools/node.dag b/dag/extdeps/tools/node.dag index af7dc25c22e..160613d6065 100644 --- a/dag/extdeps/tools/node.dag +++ b/dag/extdeps/tools/node.dag @@ -1,7 +1,6 @@ module extdeps.tools.node import std.types { NonEmptyStr, Bool, Unit } -import std.string_type { String } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/tools/npm.dag b/dag/extdeps/tools/npm.dag index 7b6ba5d572b..0ef3353edd3 100644 --- a/dag/extdeps/tools/npm.dag +++ b/dag/extdeps/tools/npm.dag @@ -1,7 +1,6 @@ module extdeps.tools.npm import std.types { NonEmptyStr, Bool, FilePath, Unit } -import std.string_type { String } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/tools/rustfmt.dag b/dag/extdeps/tools/rustfmt.dag index 1d65bc6d7ce..65bfdde4318 100644 --- a/dag/extdeps/tools/rustfmt.dag +++ b/dag/extdeps/tools/rustfmt.dag @@ -4,7 +4,6 @@ import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.tools { CliTool, SourceRustup } import std.types { Bool, FilePath, Unit } -import std.string_type { String } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dag/extdeps/tools/sed.dag b/dag/extdeps/tools/sed.dag index 5df181af2a0..6005aef0a76 100644 --- a/dag/extdeps/tools/sed.dag +++ b/dag/extdeps/tools/sed.dag @@ -1,7 +1,6 @@ module extdeps.tools.sed import std.types { NonEmptyStr, Bool, Int, Unit } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.exec.command { ArgvCommand, argv_command } diff --git a/dag/extdeps/tools/sha256sum.dag b/dag/extdeps/tools/sha256sum.dag index 3c052be38c0..56d3f66fa08 100644 --- a/dag/extdeps/tools/sha256sum.dag +++ b/dag/extdeps/tools/sha256sum.dag @@ -1,7 +1,6 @@ module extdeps.tools.sha256sum import std.types { NonEmptyStr, Bool, Unit, List } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.tools { CliTool, SourceCoreutils } diff --git a/dag/extdeps/tools/sha512sum.dag b/dag/extdeps/tools/sha512sum.dag index f9dcf9c7d21..0459da7a603 100644 --- a/dag/extdeps/tools/sha512sum.dag +++ b/dag/extdeps/tools/sha512sum.dag @@ -3,7 +3,6 @@ module extdeps.tools.sha512sum import std.algebra { trim } import std.types { NonEmptyStr, Bool, FilePath, Unit } -import std.string_type { String } import std.content_hash { Sha512Digest, sha512_hex_digest } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } diff --git a/dag/extdeps/tools/sleep.dag b/dag/extdeps/tools/sleep.dag index f0388522a07..ea550c67bbe 100644 --- a/dag/extdeps/tools/sleep.dag +++ b/dag/extdeps/tools/sleep.dag @@ -1,7 +1,6 @@ module extdeps.tools.sleep import std.types { NonEmptyStr, Bool, Unit, List } -import std.string_type { String } import std.measure { Second, second_count } import std.disposition { Disposition, Scaffold, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/extdeps/tools/stat.dag b/dag/extdeps/tools/stat.dag index 16528e2d90f..26d98b5c0c7 100644 --- a/dag/extdeps/tools/stat.dag +++ b/dag/extdeps/tools/stat.dag @@ -3,7 +3,6 @@ module extdeps.tools.stat import std.types { Bool, NonEmptyStr, List, Unit, Int } import std.algebra { trim } import extdeps.shell.exec { ProcessOutcome, ProcessOutputPresent, ProcessOutputAbsent, ProcessRefused } -import std.string_type { String } import extdeps.external_authority { ExternalAuthority, ExternalModelScope, ExternalSubjectRef } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.uri { Uri, Https } diff --git a/dag/extdeps/tools/xorriso.dag b/dag/extdeps/tools/xorriso.dag index a0897197459..c73b0e01a87 100644 --- a/dag/extdeps/tools/xorriso.dag +++ b/dag/extdeps/tools/xorriso.dag @@ -1,7 +1,6 @@ module extdeps.tools.xorriso import std.types { NonEmptyStr, Unit } -import std.string_type { String } import extdeps.version { VersionConstraint } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } diff --git a/dag/gunbc/cli_services.dag b/dag/gunbc/cli_services.dag index 314131ab6d8..d5acec1e41c 100644 --- a/dag/gunbc/cli_services.dag +++ b/dag/gunbc/cli_services.dag @@ -1,7 +1,6 @@ module gunbc.cli_services import std.types { FilePath, Unit } -import std.string_type { String } import gunbc.cli_invoke { claim_executor_verify_artifacts_transport_argv, gunbc_run_transport_argv diff --git a/dag/gunbc/fleet_observation/capture_chunk.dag b/dag/gunbc/fleet_observation/capture_chunk.dag index dd7bda02b7c..4f348b56307 100644 --- a/dag/gunbc/fleet_observation/capture_chunk.dag +++ b/dag/gunbc/fleet_observation/capture_chunk.dag @@ -1,7 +1,6 @@ module gunbc.fleet_observation_capture_chunk import std.types { Bool, Int, List, NonEmptyStr } -import std.string_type { String } import std.nat { Nat } import std.measure { ByteSize, byte_size, byte_size_count } import std.content_hash { diff --git a/dag/gunbc/fleet_observation/collector_ownership.dag b/dag/gunbc/fleet_observation/collector_ownership.dag index 9b64c7d54b2..e531d9d3c21 100644 --- a/dag/gunbc/fleet_observation/collector_ownership.dag +++ b/dag/gunbc/fleet_observation/collector_ownership.dag @@ -1,7 +1,6 @@ module gunbc.fleet_observation_collector_ownership import std.types { Bool, Int, NonEmptyStr } -import std.string_type { String } import std.decl_ref { DeclarationRef } import std.temporal_effect { HeldLease, diff --git a/dag/gunbc/fleet_observation/collector_supervision.dag b/dag/gunbc/fleet_observation/collector_supervision.dag index e12e8f09fb1..20338a1a9e0 100644 --- a/dag/gunbc/fleet_observation/collector_supervision.dag +++ b/dag/gunbc/fleet_observation/collector_supervision.dag @@ -1,7 +1,6 @@ module gunbc.fleet_observation_collector_supervision import std.types { Bool, Int, NonEmptyStr } -import std.string_type { String } import std.nat { Nat } import std.measure { ByteSize, diff --git a/dag/gunbc/fleet_observation/observation_envelope.dag b/dag/gunbc/fleet_observation/observation_envelope.dag index a07c67cefaa..9f6c7d559f6 100644 --- a/dag/gunbc/fleet_observation/observation_envelope.dag +++ b/dag/gunbc/fleet_observation/observation_envelope.dag @@ -1,7 +1,6 @@ module gunbc.fleet_observation_observation_envelope import std.types { Bool, Int, List, NonEmptyStr } -import std.string_type { String } import std.nat { Nat } import std.decl_ref { DeclarationRef } import std.measure { ObservationInstant } diff --git a/dag/gunbc/machine_intake/sol_hold.dag b/dag/gunbc/machine_intake/sol_hold.dag index e32f9e4f14b..9231c3d56f2 100644 --- a/dag/gunbc/machine_intake/sol_hold.dag +++ b/dag/gunbc/machine_intake/sol_hold.dag @@ -1,7 +1,6 @@ module gunbc.machine_intake_sol_hold import std.types { Bool, NonEmptyStr, Unit } -import std.string_type { String } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.dissolution { DissolutionCondition, unbound_dissolution } diff --git a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag index 0d1c98cd513..2f56010216d 100644 --- a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag +++ b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag @@ -298,6 +298,7 @@ data accepted_source_emits_uncompilable_target: RecurringFailureMode = Recurring "OCCURRENCE, 2026-09-24 (wise-hawk-615, routed from stern-raven-24's native App Attest row gunbc#12251, whose emitted crate refused at rustc with 45 errors in three emitter classes and one runtime-library gap). Each was worked to its earliest unjustified boundary (DESIGN section 6b) rather than at the call site. (C) STRING ORDERING: `a < b` over Timestamp, an alias of String (extdeps.standards.rfc_5280 x509_validity_at, gunbc.auth.approval_capability utc_instant_before). Timestamp comparison needed no new typed ordering: the interpreter already orders Strings byte-lexicographically, so the defect was the emitter's. The boundary was the operand classifier v1.compiler.emit_rust rust_operand_realization_of_type, which filed the corpus String as OperandIdentityUnavailable although the type renderer realizes it as the host text carrier by is_host_text_carrier_type; ordering then refused by operator class while `==` on the same operand passed. Repair: std.operator_realization HostRealizationReason gains HostTextCarrier and the classifier reads the renderer's own predicate, so both answer one question once; is_string_comparison admits the four ordering operators through the same host-string seam as equality, and an ordering over an OPTIONAL String refuses at emission (the interpreter has no Null-vs-Str ordering; Option's None-first order would be fabricated). (A) PRESENT BINDING: `match opt { null => a o => b }` bound the whole Option in emitted code (E0308, E0609) while v1.compiler.infer narrows `o` to the present value. ALL THREE match renderings now read the checker's optional_scrutinee_binding_is_present over match_unguarded_absent_arm_index and emit `Some(o)` in exactly that arm, never re-deriving the narrowing: emit_typed_match_arm_strs for ordinary matches, and emit_typed_tco_match_arm for matches inside the tail-call lowering, which the native App Attest run surfaced separately (extdeps.standards.rfc_5280 read_extensions_list, the one error left after the first renderer was repaired); and emit_typed_match's String-literal re-emission (needs_string_from), where a review of this PR found the decision dropped -- there a string-literal arm over an optional scrutinee also emits `Some(ref __s)`, since a literal matches only a present value. (D) OCTETS: std.bytes bytes_octets and utf8_encode_bytes were interpreter-intercepted builtins whose .dag bodies were placeholders -- `[pure_dag_seam_unreachable()]`, a one-element list of `1 / 0`, and `s as Bytes`, a cast no Rust row realizes and which emitted a runtime panic. Both are now declared HostRealizedSeams (self-call bodies, std.primitive_projection rows, std.primitive_identity declarations with SourcePreservingOrder traversal facts, extdeps.languages.rust.emit rt_function_registry rows, v1.runtime_rust bodies); bytes_octets is typed List under the bounded_natural_arithmetic_evaluated_as_unbounded_int ruling, since UInt8 has no emitted realization that holds an octet and bytes_qualified_octets is the one boundary observing the range. Two further links surfaced by execution: a REALIZED seam's self-call was lowered by the name-keyed tail-call rewrite into a non-terminating loop (rust_host_seam_is_realized now exempts it, see host_seam_self_call_diverges_when_its_arm_is_absent), and rustc's deny-by-default unconditional_panic refused every crate reaching std.bytes because of pure_dag_seam_unreachable's constant `1 / 0`. The first cut relaxed that lint for all generated code; review 71098 refused it as widening a real check to admit one placeholder, and the placeholder was the earlier link: pure_dag_seam_unreachable is now a rostered HostRealizedSeam too, refusing in the interpreter with a typed error naming the seam and panicking by name in v1_rt, so the lint stays on. EVIDENCE: the fixture test.fixture.emitted_interpreted_parity.string_order_octets_present_binding exits 0 interpreted and requires the thirteen-field line `T F T F T 6 2962370309 97 0 989 ex none q` -- fields 1-5 string ordering, 6-7 octets, 8-9 the ordinary-match present binding, 10 the tail-call-match present binding, 11-13 the String-literal re-emission -- with a RED control that exits 1 printing the observed line; its emitted crate builds and runs to the same line. The enrolled claim module test.claim.emitter_string_order_present_binding_witness_test carries the emission half: host-string ordering over an alias, the named refusal for an ordering over a String? (which inference admits today, so the arm is reachable), and the present binding in all three match renderings. The integration consumer is `gunbc test //gunbc/instruments:native-app-attest` on gunbc#12251. RUNG unchanged at mitigatable for the class: required CI still neither emits-and-compiles a fixture closure nor executes integration targets, which is this row's existing trigger.", "OCCURRENCE, 2026-09-25 (loyal-boar-23, found by swift-bat-511 on native App Attest P2b): CALLING A LOCALLY BOUND FUNCTION VALUE. extdeps.apple.app_attest first_assertion_refusal folded a List AssertionRefusal?> and called the lambda-bound element `r()`; gunbc#12251 rewrote that one fold as a named AssertionStep sum, leaving the idiom refused everywhere else. The brief placed the defect in emit_rust's callee resolution; re-deriving the chain (DESIGN 6b) placed it three links earlier and found two more downstream. (1) INFERENCE, the earliest unjustified boundary: v1.compiler.infer child_type_node, the total read of a child position's type, returned a child's inferred slot whenever it had one, and an Arrow type node carries its RETURN type there (make_callable_type) -- so the element of List Int> was Int, a lambda parameter bound under it was not callable, and the call refused as \"function 'r' not found in scope\" in `gunbc run` as well as in emission. An Arrow child is now its own type -- landed independently by gunbc#12295 (receipted on arity_proxy_for_callability_misroutes_a_found_local) while this change was in review; this change consumes it rather than restating it. (2) INFERENCE, the list literal: a lambda's typed node carries its BODY type, and a list literal took its first element's type, so [fn() { none }] against List Int?> was typed List; where the declared element is an arrow and any element is a lambda the declared arrow is the element type, every element still judged against it. (3) EMISSION, two value positions of a lambda: a lambda literal in a list was a bare closure (E0308, every closure its own type), and is now the arrow carrier through the record-field row (rust_callable_field_value_wrap) bound at the rendered arrow by a typed let, not an `as` cast, which cuts the expectation typing Some(7) as Option (E0271); a let-bound lambda was `|x|` with no call site to type x from (E0282 at x.clone()) and is now emitted with typed parameters through emit_typed_collection_lambda, staying a bare closure so it still enters an impl Fn parameter. The call sites themselves (r(), f(v), g(v)) needed no change. EVIDENCE: test.fixture.emitted_interpreted_parity local_callable_value_calls exits 0 interpreted and its emitted crate, driven by a two-line harness over its library, prints the same line `6 8 6 7 9 0` with main returning ExitSuccess; with the expected line mutated both sides refuse (interpreter exit 1, emitted main ExitFailure carrying the observed line); on the pre-fix seed the fixture refuses at resolve. test.claim.local_callable_value_call_witness_test carries three inference controls (fold, unary fold and map lambda parameters over callable elements) and two emission controls (the list-literal carrier and the typed let), all five FAIL on the pre-fix seed and PASS here. RESIDUE, stated: an UNANNOTATED let-bound lambda (`let h = fn(x) { x * 2 }`) has no parameter type a Rust closure can be written at and still emits `|x: _|`-equivalent (E0282); a non-lambda element (a let-bound closure variable) in a List keeps its bare rendering. RUNG unchanged at mitigatable: required CI still neither emits-and-compiles a fixture closure nor runs its crate, which is this row's existing trigger.", "OCCURRENCE, 2026-09-24 (lively-koi-275, gunbc#12208 MQ-1): A FIELD BOUND UNDER A NESTED VARIANT PATTERN KEEPS ITS BOX. The `diagnostics` field of `v2.std.diagnostic` `Outcome` is a recursive Optional, so `v1.compiler.emit_rust` `needs_box_wrapping` boxes it. At a SINGLE-LEVEL arm `Accepted { value: v, diagnostics: d } =>` the emitter binds the field by `ref` and unboxes it (`let d = diagnostics.as_ref()`). Under a NESTED arm `Present { value: Accepted { value: facts, diagnostics: d } } =>` it bound the Box itself, and passing `d` to `v2.std.diagnostic` `diagnostics_merge` failed rustc with E0308 (expected `Option>`, found `Box>`) in the emitted `v2.compiler.infer`. The `.dag` was accepted with zero diagnostics, and only the non-required emit-build lane (`//gunbc/instruments:self-host`) saw it. The author-side repair split the match into two single-level arms, which the rest of the corpus already does by habit. That habit is an unstated workaround: it explains why the population is small and is not evidence that it is zero. The existing trigger of this row covers it: the emitted carrier comes from the declaration, and nested-pattern binders must go through the same unboxing join as single-level arms.", + "THE std.string_type COMPONENT IS DISCHARGED, 2026-09-28 (royal-newt-820, XL-0T ruling B): it now REFUSES AT CHECK in the shape it had, and its eight E0308 are gone because the shape no longer exists in the corpus. The link that was unjustified was neither of the two this row called justified -- it was the compatibility relation, which matched `String` against `String` by leaf spelling across two representations. The repair is one classifier, v1.compiler.coercion text_representation_of_type, read by the checker (corpus relation, call arguments, declared returns and data initializers, and builtin arguments, which had been admitted untyped) and by the Rust renderer. So a local `type String = FreeMonoid` (or a renamed `type Text = FreeMonoid`) handed to string_length/char_at/substring is a located refusal: test.claim.text_boundary_identity_wall_witness_test r_local_string_alias_into_kernel_primitive_refuses and r_renamed_text_alias_into_kernel_primitive_refuses. std.string_type itself keeps its two functions over the kernel String -- which is what their bodies always were -- and loses the structural declaration; its 71 importers were nicknaming host text as that declaration and their imports are deleted, including src/v2/compiler/discovery_enumeration.dag, whose emitted closure no longer contains a structural std.string_type. THE TWO OPEN SHAPES THE ENTRY ABOVE ROUTED FOR A RULING were decided as B (two carriers, a declared crossing): a host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node keyed to the existing literal_homomorphism_rows row, and every other crossing refuses. The residue and its trigger are carried by gunbc.rung_drop text_boundary_identity_wall, not restated here.", ], diff --git a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag index fdf9db1a464..8a49cf6716f 100644 --- a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag +++ b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag @@ -12,5 +12,5 @@ data text_boundary_identity_wall: RungDrop = RungDrop { standing: Standing, - declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's." } + declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it), std.string_type's own `type String = FreeMonoid` is deleted because its bodies are kernel host-text primitives, and v1.compiler.coercion structural_declaration_modules_for loses the dag/std/string_type.dag entry. ONE SEED CONSEQUENCE, REPAIRED WITH IT: with the duplicate gone, v1.compiler.infer_env global_bare_lookup found ONE corpus String and bound every service-output field spelled String to v2.std.text from anywhere; a kernel spelling now never resolves through the global bare fallback. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } } diff --git a/dag/std/coercion.dag b/dag/std/coercion.dag index 114d91fd9c4..c3d062e7167 100644 --- a/dag/std/coercion.dag +++ b/dag/std/coercion.dag @@ -111,6 +111,15 @@ type TypeDeclarationProvenance | KernelMinted { minted_name: String } | DeclarationIdentityAbsent +// THE TWO REPRESENTATIONS OF TEXT (operator Ruling 3, 2026-07-15; XL-0T ruling B). Kernel String is +// HOST TEXT; a FreeMonoid declared in the corpus is a CODE-POINT SEQUENCE. One type, two +// representations, and the only declared route between them is the literal homomorphism +// (gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold). A +// value of one representation at a position of the other is therefore a crossing with no route, +// and the type-compatibility relation refuses it rather than matching the two by spelling. +// NotText is every other type: this classification says nothing about it. +type TextRepresentation = HostText | CodePointSequence | NotText + // WHICH STATE A TYPE REFERENCE IS IN WHEN ITS DECLARATION IS ASKED FOR -- the split of the one arm // the reference-site read used to answer for two states. v1.std.core type_reference_provenance // falls back to the reference node's OWN span whenever no Resolved node is bound, so `this node IS diff --git a/dag/std/markup.dag b/dag/std/markup.dag index a9e691a0e33..011dc66ce16 100644 --- a/dag/std/markup.dag +++ b/dag/std/markup.dag @@ -1,6 +1,5 @@ module std.markup -import std.string_type { String } type EscapeContext = TextContext | AttributeContext | UrlContext diff --git a/dag/std/string_type.dag b/dag/std/string_type.dag index 8932bed8d62..e05cf20f499 100644 --- a/dag/std/string_type.dag +++ b/dag/std/string_type.dag @@ -1,9 +1,15 @@ module std.string_type -import std.algebra { FreeMonoid, Ordering, Less, Equal, Greater, ordering_is_less } -import std.types { Char } +import std.algebra { Ordering, Less, Equal, Greater, ordering_is_less } -type String = FreeMonoid +// LEXICOGRAPHIC ORDER OVER HOST TEXT. The parameters are the kernel String because the bodies +// are the kernel string primitives (string_length, char_at, substring, code_point), whose contract +// is host text. This module once also declared `type String = FreeMonoid` and typed these +// same parameters with it, so every call to a primitive was a crossing from the code-point +// sequence to host text with no unfold -- accepted by the checker and refused by rustc (eight +// E0308 in string_lex_compare). The structural text carrier is v2.std.text String; a second +// structural declaration here had no consumer that used it structurally +// (gunbc.rung_drop text_boundary_identity_wall). fn string_lex_compare(left: String, right: String) -> Ordering { let left_len = string_length(s: left) diff --git a/dag/test/claim/fleet_observation_capture_witness_test.dag b/dag/test/claim/fleet_observation_capture_witness_test.dag index 6ef6ca0879e..e8e5434e9f5 100644 --- a/dag/test/claim/fleet_observation_capture_witness_test.dag +++ b/dag/test/claim/fleet_observation_capture_witness_test.dag @@ -1,7 +1,6 @@ module test.claim.fleet_observation_capture_witness import std.types { Bool, NonEmptyStr } -import std.string_type { String } import std.nat { Nat } import std.measure { ByteSize, byte_size, ObservationInstant, ClockDomain, ProcessMonotonic, SharedMonotonic, ClockBasis, WallClock } import std.decl_ref { DeclarationRef, WholeDeclaration } diff --git a/dag/test/claim/machine_intake/mtcollins1_census_image_witness_test.dag b/dag/test/claim/machine_intake/mtcollins1_census_image_witness_test.dag index 8b50f8e8402..7f9dd4a8bfa 100644 --- a/dag/test/claim/machine_intake/mtcollins1_census_image_witness_test.dag +++ b/dag/test/claim/machine_intake/mtcollins1_census_image_witness_test.dag @@ -3,7 +3,6 @@ module test.claim.mtcollins1_census_image import gunbc.machine_intake_mtcollins1_boot_milestone { mtcollins1_census_stages, mtcollins1_one_socket_workload, mtcollins1_dual_socket_workload } import std.logic { Bool } import std.types { Int, List, NonEmptyStr } -import std.string_type { String } import extdeps.provisioning.ubuntu_seeded_install_media { NoCloudSeedFile, SeededInstallMediaPinnedDates, diff --git a/dag/test/claim/self_host_structural_text_witness_test.dag b/dag/test/claim/self_host_structural_text_witness_test.dag index 1ecf51083aa..875597b75bf 100644 --- a/dag/test/claim/self_host_structural_text_witness_test.dag +++ b/dag/test/claim/self_host_structural_text_witness_test.dag @@ -1,6 +1,8 @@ module test.claim.self_host_structural_text_witness_test import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.algebra { any } +import gunbc.compile_diagnostic_census { CompileDiagnosticCensusRow, CensusObserved, CensusNotRunnable } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly @@ -120,24 +122,22 @@ test fn w_literal_at_symbol_boundary_still_emits_host_string() -> Bool { ) } -// RESTORATION PROBE FOR THE DECLARED RUNG DROP gunbc.rung_drop text_boundary_identity_wall -- -// THIS ROW ASSERTS THE GAP, NOT THE GUARANTEE. A NON-LITERAL kernel-String value (the result of -// the kernel concat builtin) offered at an exact qualified v2.std.text.String return boundary is -// PRESENTLY ACCEPTED by same-leaf-name compatibility, with no homomorphism row and no refusal. -// That acceptance is the drop's bounded observation: deleting this row would turn the bounded gap -// into an unobserved one. WHEN the declared-boundary conformance peeling capability lands and the -// identity wall is re-landed under the row's two-direction A/B, this probe REDS (the fixture -// refuses); it is then flipped to the discriminating refusal control -- asserting the typed -// refusal via the diagnostic census -- and the literal rows above plus the grounded-identity -// controls must stay green in the same run. Passing the flip by restoring the roadmap_page -// false positive is not the trigger. -test fn w_nonliteral_kernel_string_at_text_boundary_is_presently_accepted_declared_drop() -> Bool { - compile_dag_rust_emit_check( - "module test.claim.text_nonliteral_probe\nimport v2.std.text { string_is_empty }\nfn probe() -> v2.std.text.String { concat(\"x\", \"y\") }\n", - "src/test_claim_text_nonliteral_probe.rs", - ["pub fn probe"], - [] - ) +// THE FLIPPED RESTORATION PROBE (gunbc.rung_drop text_boundary_identity_wall; DESIGN 4b(4)). This +// row used to assert the GAP: a non-literal kernel-String value (the kernel concat builtin's result) +// at an exact qualified v2.std.text.String return boundary was accepted with no homomorphism and no +// refusal. The wall now keys on text representation rather than leaf spelling +// (v1.compiler.coercion text_representation_of_type), and a declared return carries no unfold, so +// the same fixture is a blocking refusal. It stays enrolled as the discriminating control that the +// wall is real; its paired positive controls are the literal rows above and +// test.claim.text_boundary_identity_wall_witness_test. +test fn w_nonliteral_kernel_string_at_text_return_boundary_refuses() -> Bool { + match compile_dag_diagnostic_census("module test.claim.text_nonliteral_probe\nimport v2.std.text { string_is_empty }\nfn probe() -> v2.std.text.String { concat(\"x\", \"y\") }\n") { + CensusObserved { rows: rows } => { + let rs: List = rows + rs |> any(r => r.blocking) + } + CensusNotRunnable { cause: _ } => false + } } // OP REALIZATION OVER THE STRUCTURAL CARRIER IS THE DECLARED BODIES: the emitted v2_std_text diff --git a/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag b/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag index 66d9aefa43a..93d8d690174 100644 --- a/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag +++ b/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag @@ -62,14 +62,14 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // THE SAME-SPELLING SIBLINGS, all REAL declarations (the cited-declaration gate refuses a citation // of a declaration that does not exist, so the "another module declaring Symbol" falsifier is -// asked of the declarations the corpus actually has two of): v2.std.text String and std.string_type -// String beside the kernel String row, v2.std.logic Bool beside std.types Bool (which HAS an exact +// asked of the declarations the corpus actually has two of): v2.std.text String beside the kernel +// String row (std.string_type's second structural String was deleted with its nickname importers, +// gunbc.rung_drop text_boundary_identity_wall), v2.std.logic Bool beside std.types Bool (which HAS an exact // row), and v2.std.node Hash, declared in Symbol's own module one line below it. None may receive a // binding authored for a different declaration -- module-path and decl-name discrimination both. fn sibling_declarations_without_bindings() -> List { [ decl_ref(module_path: "v2.std.text", decl_name: "String"), - decl_ref(module_path: "std.string_type", decl_name: "String"), decl_ref(module_path: "v2.std.logic", decl_name: "Bool"), decl_ref(module_path: "v2.std.node", decl_name: "Hash") ] diff --git a/dag/test/claim/text_boundary_identity_wall_witness_test.dag b/dag/test/claim/text_boundary_identity_wall_witness_test.dag new file mode 100644 index 00000000000..c296c139788 --- /dev/null +++ b/dag/test/claim/text_boundary_identity_wall_witness_test.dag @@ -0,0 +1,151 @@ +module test.claim.text_boundary_identity_wall_witness_test + +import std.types { String, Bool, Int, List } +import v2.std.live_tree { LiveTreeDisposition, ReadsLiveTree } +import v2.std.algebra { filter } +import gunbc.compile_diagnostic_census { + CompileDiagnosticCensus, CompileDiagnosticCensusRow, CensusObserved, CensusNotRunnable, +} + +data live_tree_disposition: LiveTreeDisposition = ReadsLiveTree + +// THE TWO TEXT REPRESENTATIONS MEET ONLY THROUGH THE UNFOLD, OR THE CROSSING REFUSES +// (gunbc.rung_drop text_boundary_identity_wall, XL-0T ruling B). Kernel String is host text and a +// FreeMonoid is a code-point sequence; the one declared route between them is the Unicode +// scalar sequence of the text (gunbc.structural_realization_bindings literal_homomorphism_rows +// UnicodeScalarSequenceUnfold for a literal; the scalar-string `chars` method for a value at a call +// argument). r_ rows hand one representation's value to the other's position where no route exists +// -- a code-point sequence into host text in either spelling of the destination, and a host value +// at a declared return or data initializer -- and each must be a blocking refusal however the +// destination is spelled and whichever module resolved it. u_ rows assert the unfold's ROUTE in +// the emitted bytes, not just an admission. c_ rows are the same shapes with no crossing, so a +// wall that refuses by shape rather than by representation reds a control. + +fn blocking_total(c: CompileDiagnosticCensus) -> Int { + match c { + CensusNotRunnable { cause: _ } => -1 + CensusObserved { rows: rows } => { + let rs: List = rows + rs |> filter(r => r.blocking) |> fold(init: 0, f: (acc, r) => acc + r.count) + } + } +} + +fn refuses(source: String) -> Bool { + blocking_total(c: compile_dag_diagnostic_census(source)) > 0 +} + +fn admitted(source: String) -> Bool { + blocking_total(c: compile_dag_diagnostic_census(source)) == 0 +} + +// THE SHAPE std.string_type HAD: a local `type String = FreeMonoid` whose own body hands the +// value to a kernel host-text primitive. The checker admitted it and rustc refused the emission +// (eight E0308 in string_lex_compare). +test fn r_local_string_alias_into_kernel_primitive_refuses() -> Bool { + refuses(source: "module probe_local_string_alias\nimport std.algebra { FreeMonoid }\nimport std.types { Char }\ntype String = FreeMonoid\nfn text_len(t: String) -> Int { string_length(s: t) }\n") +} + +// The same module with the alias renamed, so no kernel spelling is in the module at all: the +// refusal is about the representation, not the name String. +test fn r_renamed_text_alias_into_kernel_primitive_refuses() -> Bool { + refuses(source: "module probe_renamed_text_alias\nimport std.algebra { FreeMonoid }\nimport std.types { Char }\ntype Text = FreeMonoid\nfn text_len(t: Text) -> Int { string_length(s: t) }\n") +} + +test fn r_qualified_structural_value_into_kernel_primitive_refuses() -> Bool { + refuses(source: "module probe_qualified_into_kernel\nimport v2.std.text { string_is_empty }\nfn text_len(t: v2.std.text.String) -> Int { string_length(s: t) }\n") +} + +// THE DISCRIMINATING RED THE DROP NAMES: a non-literal kernel String at a structural position +// that carries no unfold. A declared return is such a position (only a call argument unfolds). +test fn r_kernel_value_at_qualified_text_return_refuses() -> Bool { + refuses(source: "module probe_kernel_return_qualified\nimport v2.std.text { string_is_empty }\nfn back(s: String) -> v2.std.text.String { s }\n") +} + +// ALIASING: the destination names the guarded declaration through a local alias. +test fn r_kernel_value_at_alias_of_text_return_refuses() -> Bool { + refuses(source: "module probe_kernel_return_alias\nimport v2.std.text { string_is_empty }\ntype LocalText = v2.std.text.String\nfn back(s: String) -> LocalText { s }\n") +} + +// ALIASING, STRUCTURE-IDENTICAL: a locally authored FreeMonoid that never names v2.std.text. +test fn r_kernel_value_at_structure_identical_alias_return_refuses() -> Bool { + refuses(source: "module probe_kernel_return_wrapped\nimport std.algebra { FreeMonoid }\nimport std.types { Char }\ntype Wrapped = FreeMonoid\nfn back(s: String) -> Wrapped { s }\n") +} + +// A data initializer is the other declared position without an unfold. +test fn r_kernel_value_at_text_data_initializer_refuses() -> Bool { + refuses(source: "module probe_kernel_data_qualified\nimport v2.std.text { string_is_empty }\ndata host: String = \"hi\"\ndata seq: v2.std.text.String = host\n") +} + +// THE UNFOLD AT A CALL ARGUMENT, asserted on the ROUTE and not only the verdict: a non-literal host +// value at a code-point-sequence formal is admitted BECAUSE the typed tree carries the Unicode +// scalar unfold (the scalar-string `chars` method), which the emitted Rust shows; the must_not_contain +// is the bare pass-through that rustc refused as E0308. +test fn u_kernel_value_at_qualified_text_parameter_unfolds() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_unfold_qualified_probe\nimport v2.std.text { string_is_empty }\nfn take(t: v2.std.text.String) -> Int { 0 }\nfn host_into(s: String) -> Int { take(t: s) }\n", + "src/test_claim_text_unfold_qualified_probe.rs", + [".chars().map(|c| c as i64)"], + ["take(s)", "take(s.clone())"] + ) +} + +// ALIASING at a call argument: the formal names the guarded declaration through a local alias. +test fn u_kernel_value_at_alias_of_text_parameter_unfolds() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_unfold_alias_probe\nimport v2.std.text { string_is_empty }\ntype LocalText = v2.std.text.String\nfn take(t: LocalText) -> Int { 0 }\nfn host_into(s: String) -> Int { take(t: s) }\n", + "src/test_claim_text_unfold_alias_probe.rs", + [".chars().map(|c| c as i64)"], + ["take(s)", "take(s.clone())"] + ) +} + +// CALLER RE-RESOLUTION: v2.std.text string_is_empty spells its parameter with the bare String, which +// is structural in its declaring module. A foreign caller's kernel value is unfolded against the +// DECLARATION-BOUND formal, not passed through as the caller's reading of the spelling. +test fn u_kernel_value_at_foreign_bare_structural_parameter_unfolds() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_unfold_caller_probe\nimport v2.std.text { string_is_empty }\nfn host_empty(s: String) -> v2.std.logic.Bool { string_is_empty(s: s) }\n", + "src/test_claim_text_unfold_caller_probe.rs", + [".chars().map(|c| c as i64)"], + ["string_is_empty(s)", "string_is_empty(s.clone())"] + ) +} + +// And a LITERAL at that same foreign bare formal takes the literal homomorphism (scalars 104, 105), +// which it did not while the formal was re-resolved in the caller as host text. +test fn u_kernel_literal_at_foreign_bare_structural_parameter_unfolds() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_unfold_caller_literal_probe\nimport v2.std.text { string_is_empty }\nfn probe() -> v2.std.logic.Bool { string_is_empty(s: \"hi\") }\n", + "src/test_claim_text_unfold_caller_literal_probe.rs", + ["vec![104, 105]"], + ["\"hi\".to_string()"] + ) +} + +// CONTROLS -- the same shapes with no crossing. + +test fn c_kernel_value_into_kernel_primitive_is_admitted() -> Bool { + admitted(source: "module probe_kernel_into_kernel\nfn host_len(s: String) -> Int { string_length(s: s) }\n") +} + +test fn c_structural_value_at_structural_parameter_is_admitted() -> Bool { + admitted(source: "module probe_structural_into_structural\nimport v2.std.text { string_is_empty }\nfn take(t: v2.std.text.String) -> Int { 0 }\nfn pass(u: v2.std.text.String) -> Int { take(t: u) }\n") +} + +test fn c_structural_value_at_foreign_structural_parameter_is_admitted() -> Bool { + admitted(source: "module probe_structural_into_foreign\nimport v2.std.text { string_is_empty }\nfn probe(t: v2.std.text.String) -> v2.std.logic.Bool { string_is_empty(s: t) }\n") +} + +// The literal half stays on its homomorphism: a kernel LITERAL at a structural position is the +// declared unfold, not a crossing. +test fn c_kernel_literal_at_qualified_text_parameter_is_admitted() -> Bool { + admitted(source: "module probe_literal_into_qualified\nimport v2.std.text { string_is_empty }\nfn take(t: v2.std.text.String) -> Int { 0 }\nfn probe() -> Int { take(t: \"hi\") }\n") +} + +// THE roadmap_page script_src_attr JOIN, through the REAL std.markup Attribute: a field value +// joined with a kernel "" accumulator in an if. Admitted because std.markup's value is the kernel +// String now that its std.string_type import is deleted -- the crossing is gone, not exempted. +test fn c_markup_attribute_value_if_join_is_admitted() -> Bool { + admitted(source: "module probe_script_src_attr\nimport std.markup { Attribute }\nfn script_src_attr(attributes: List) -> String {\n fold(attributes, init: \"\", f: (acc, a) => if a.name == \"src\" { a.value } else { acc })\n}\n") +} diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index c6517248dcf..37b4b670881 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -992,7 +992,19 @@ fn record_global_bare_ambiguous_silent_pick( } } +// A KERNEL SPELLING NEVER BINDS A CORPUS DECLARATION THROUGH THE GLOBAL BARE FALLBACK -- kernel +// precedence (v1.compiler.infer direct_import_export_precedence_note) applied to the last tier. The +// outcome for String used to hold by accident: std.string_type and v2.std.text both declared it, +// the entry was ambiguous, and nearest-ancestor containment answered nothing for any module outside +// those two. Deleting std.string_type's duplicate (gunbc.rung_drop text_boundary_identity_wall) left +// one candidate, which a unique entry binds from anywhere -- so every service output field spelled +// `String` became v2.std.text's code-point sequence. The rule is stated here so it no longer depends +// on how many modules happen to reuse a kernel spelling. fn global_bare_lookup(env: TypeEnv, name: String) -> TypeBinding? { + if is_kernel_type(name: name) { none } else { global_bare_lookup_candidates(env: env, name: name) } +} + +fn global_bare_lookup_candidates(env: TypeEnv, name: String) -> TypeBinding? { match map_get(env.symbol_index.global_bare, name) { Present { value: GlobalBareUniqueBinding { module_path: _, binding: binding } } => Present { value: binding } Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => { diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index a1d5ab4692d..cc0f06acb65 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -9,7 +9,7 @@ import std.content_hash { } import extdeps.container.oci.digest { oci_other_digest_algorithm, oci_other_digest_encoded } import std.syntax { Add, Sub, Mul, Div, Mod, Eq, Ne, Lt, Gt, Le, Ge, And, Or, NullCoalesce, BinOp, LitStr, LitInt, LitBool, LiteralValue } -import std.decl_ref { DeclarationRef } +import std.decl_ref { DeclarationRef, decl_ref } import std.operator_realization { OperandDeclaration } import std.literal_elaboration { LiteralElaboration, LiteralHomomorphism, LiteralUnfolding, PeanoUnfold, BooleanUnfold, UnicodeScalarSequenceUnfold, @@ -17,7 +17,7 @@ import std.literal_elaboration { elaborate_literal_at, literal_source_kind_of, literal_elaboration_refusal_message } import gunbc.structural_realization_bindings { literal_homomorphism_rows } -import std.coercion { dag_can_cast, is_dag_cast_domain_type, dag_cast_requires_proof, SubtractionRefinementAmbiguous } +import std.coercion { dag_can_cast, is_dag_cast_domain_type, dag_cast_requires_proof, SubtractionRefinementAmbiguous, HostText, CodePointSequence, NotText, KernelMinted, CorpusDeclared, DeclarationIdentityAbsent } import v1.compiler.type_head_exposure { TypeHeadView, KernelScalarHead, ApplicationHead, ProductHead, CoproductHead, CallableHead, TypeHeadExposure, ExposedTypeHead, OpaqueTypeHead, StuckTypeHead, MalformedApplicationHead, @@ -113,10 +113,10 @@ import v1.compiler.infer_types { callable_return_type } import v1.compiler.infer_method { - builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type + builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, builtin_host_text_param_names, builtin_param_names } import v1.compiler.infer_cycle { detect_type_cycles_kahn } -import v1.compiler.coercion { provenance_realizes_natively } +import v1.compiler.coercion { provenance_realizes_natively, text_representation_of_type, text_representations_cross } import v1.compiler.infer_env { TypeEnv, TypeBinding, TypeEnvCache, empty_type_env_cache, merge_type_env_cache, merge_type_env_cache_guarded, GuardedTypeEnvCacheMerge, TypeEnvCacheMergeConflict, @@ -1468,11 +1468,25 @@ fn declared_type_kernel_inhabitance_mismatch_here_or_at_element( // set -- so this is consumption of the existing authority at a second declared-type position, // not a new relation (gunbc.recurring_failure_mode declared_type_wall_keyed_on_the_value_being_a_kernel, // specimen four). +// IN declared_type_conformance_diags, A TEXT CROSSING IS PROVEN, NOT UNJUDGED: both sides' representations come from the one +// classifier (v1.compiler.coercion text_representation_of_type), so a host-text value produced +// where a code-point sequence is declared -- or the reverse -- is a real difference with no +// alias or brand between the two, and these positions carry no unfold (only a call argument +// does, v1.compiler.infer host_text_unfolded_at_code_point_boundary). gunbc.rung_drop +// text_boundary_identity_wall. fn declared_type_conformance_diags(declared: Node, produced: Node, span: SourceSpan, scope: InferScope) -> List { let si = scope.type_env.source_indices let both_ground = conformance_ground_type(n: declared, source_indices: si) && conformance_ground_type(n: produced, source_indices: si) - if declared_type_kernel_inhabitance_mismatch_here_or_at_element( + if text_representations_cross(left: declared, right: produced, source_indices: si) { + [type_mismatch_error( + expected: node_type_shape(n: declared, source_indices: si), + got: node_type_shape(n: produced, source_indices: si), + span: span, + module_name: scope.module_name + )] + } + else if declared_type_kernel_inhabitance_mismatch_here_or_at_element( declared: declared, produced: produced, scope: scope) { [type_mismatch_error( expected: node_type_shape(n: declared, source_indices: si), @@ -5493,7 +5507,8 @@ fn direct_call_arg_type_mismatch( else if direct_call_formal_has_unbound_type_variable(n: substitution_basis) || direct_call_formal_has_unbound_type_variable(n: actual) { false } else if type_node_is_callable(n: formal) || type_node_is_callable(n: actual) { false } else { - nominal_call_arg_brand_mismatch(formal: formal, actual: actual, type_env: type_env, source_indices: source_indices) + text_representations_cross(left: formal, right: actual, source_indices: source_indices) + || nominal_call_arg_brand_mismatch(formal: formal, actual: actual, type_env: type_env, source_indices: source_indices) || container_element_nominal_brand_mismatch( formal: formal, actual: actual, type_env: type_env, module_name: module_name, source_indices: source_indices) || kernel_value_declared_type_mismatch( @@ -5793,6 +5808,44 @@ data direct_call_shape_wall_note: String = "WALL (DESIGN §5) — the compile se // Record-literal actuals skip this path; handoff lives in // direct_call_structured_application_mismatch_diags (+ resolved-type legacy checks there). +// A VALUE'S TEXT REPRESENTATION IS READ FROM BOTH OF ITS TYPE VIEWS. The resolved type and its +// alias-peeled form each lose the structure in one known case: a qualified v2.std.text.String value's +// resolved node still reads as the spelling String (so only the peel sees the carrier), while a node +// typed by its declaring module's alias spelling String is re-resolved by name in the caller's +// environment by the peel (so only the resolved view sees it). Either view establishing a code-point +// sequence establishes it; host text only when neither view does -- EXCEPT that a resolved type the +// kernel minted is host text outright: the peel looks its spelling up by name, and a by-name lookup +// of the kernel spelling String may find a corpus declaration, which kernel precedence says it never +// binds to. +fn argument_text_representation(value: Node, type_env: TypeEnv, module_name: String) -> TextRepresentation { + let raw = expression_value_type(e: value) + let peeled = peel_nominal_alias_identity(n: raw, env: type_env, module_name: module_name) + let raw_rep = text_representation_of_type(n: raw, source_indices: type_env.source_indices) + let peeled_rep = text_representation_of_type(n: peeled, source_indices: type_env.source_indices) + let raw_is_kernel_mint = match type_reference_provenance(n: raw) { + KernelMinted { minted_name: _ } => true + CorpusDeclared { decl_file: _ } => false + DeclarationIdentityAbsent => false + } + match raw_rep { + CodePointSequence => CodePointSequence + HostText => if raw_is_kernel_mint { HostText } else { match peeled_rep { CodePointSequence => CodePointSequence HostText => HostText NotText => HostText } } + NotText => peeled_rep + } +} + +fn text_representation_is_text(r: TextRepresentation) -> Bool { + match r { NotText => false HostText => true CodePointSequence => true } +} + +fn text_representations_disagree(a: TextRepresentation, b: TextRepresentation) -> Bool { + match a { + NotText => false + HostText => match b { CodePointSequence => true HostText => false NotText => false } + CodePointSequence => match b { HostText => true CodePointSequence => false NotText => false } + } +} + fn direct_call_arg_mismatch_diags( plan: List, typed_args: List, @@ -5816,7 +5869,13 @@ fn direct_call_arg_mismatch_diags( } else { let actual_raw = expression_value_type(e: actual_expr) let actual = peel_nominal_alias_identity(n: actual_raw, env: type_env, module_name: module_name) - if direct_call_arg_type_mismatch( + let formal_text = if formal_code_point_view(formal: app.formal, source_indices: source_indices) != none { CodePointSequence } else { text_representation_of_type(n: formal, source_indices: source_indices) } + let actual_text = argument_text_representation(value: actual_expr, type_env: type_env, module_name: module_name) + if text_representations_disagree(a: formal_text, b: actual_text) { + [make_error_node(diagnostic: InternalError { message: concat("text representation crossing at a call argument: declared ", node_type_shape(n: formal, source_indices: source_indices), ", produced ", node_type_shape(n: actual, source_indices: source_indices), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text"), span: actual_expr.span }, module_name: module_name)] + } else if text_representation_is_text(r: formal_text) && text_representation_is_text(r: actual_text) { + [] + } else if direct_call_arg_type_mismatch( formal: formal, substitution_basis: app.formal.substitution_basis, actual: actual, actual_expr: actual_expr, type_env: type_env, module_name: module_name, source_indices: source_indices) { [type_mismatch_error( expected: node_type_shape(n: formal, source_indices: source_indices), @@ -5865,6 +5924,45 @@ type Tier2bBt { // existing two contracts distinguishable, which is the prerequisite for either answer. The name is // currently forked across v1.compiler.method, v1.compiler.languages (bridge_method_overrides maps // it to with_update), v1.compiler.parse and v1.compiler.emit_rust, each special-casing it apart. +// A CODE-POINT SEQUENCE HANDED TO A HOST-TEXT BUILTIN IS A CROSSING WITH NO ROUTE (XL-0T ruling B; +// gunbc.rung_drop text_boundary_identity_wall). The builtin registry types a call by its return +// alone, so its arguments were never judged, and a module declaring `type String = FreeMonoid` +// could call string_length / char_at / substring on its own values with zero diagnostics while +// rustc refused the emission. Each argument bound to a host-text parameter +// (v1.compiler.infer_method builtin_host_text_param_names) is asked the one text-representation +// authority (v1.compiler.coercion text_representation_of_type); a CodePointSequence there refuses, +// located at the argument. An argument is matched to its parameter by label, else by position. +fn builtin_text_argument_crossing_diags(func_name: String, typed_args: List, scope: InferScope) -> List { + let host_params = builtin_host_text_param_names(name: func_name) + if host_params |> count == 0 { [] } + else { + let formal_names = builtin_param_names(name: func_name) + typed_args |> enumerate |> flat_map(pair => { + let arg = pair.second + let bound_name = match arg_name_at(n: arg, source_indices: scope.type_env.source_indices) { + Present { value: label } => label + Absent => match formal_names |> skip(pair.first) |> first { Present { value: n } => n Absent => "" } + } + if !(host_params |> any(n => n == bound_name)) { [] } + else { + let value = arg_value(n: arg) + match value.inferred { + Present { value: Resolved { node: _ } } => + match argument_text_representation(value: value, type_env: scope.type_env, module_name: scope.module_name) { + CodePointSequence => [inference_error( + message: concat("text representation crossing: argument '", bound_name, "' of ", func_name, + " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence"), + span: value.span, module_name: scope.module_name)] + HostText => [] + NotText => [] + } + _ => [] + } + } + }) + } +} + fn infer_tier2b_builtin_with_kernel_diags(func_name: String, typed_args: List, scope: InferScope, span: SourceSpan) -> Tier2bBt { if func_name == "lookup" || func_name == "map_get" { Tier2bBt { @@ -6755,6 +6853,82 @@ fn expression_is_uppercase_constructor_reference(n: Node, source_indices: Map Bool { + match literal_boundary_elaboration(lit: LitStr { value: "" }, expected: Present { value: destination_type }, scope: scope) { + LiteralBoundaryElaborated { elaboration: e, destination_type: _ } => + match e.homomorphism.producer { + UnicodeScalarSequenceUnfold => true + _ => false + } + LiteralBoundaryPlain => false + LiteralBoundaryRefused { message: _ } => false + } +} + +// THE UNFOLD, FOR A VALUE (XL-0T ruling B; gunbc.rung_drop text_boundary_identity_wall). A call +// argument bound to a formal whose DECLARATION-BOUND type is a code-point sequence is inferred under +// that type, never under the formal's spelling re-resolved in the caller: a callee declaring its +// parameter with the bare String of v2.std.text means the structural carrier at every call site, +// and reading param_node_type_expr in the caller's scope made the same parameter host text there. +// A kernel literal then takes its declared homomorphism (literal_boundary_elaboration). A +// NON-literal host-text value takes the SAME unfold, selected by the SAME row +// (destination_declares_scalar_sequence_unfold), as a node in the typed tree: the scalar-string +// algebra method `chars` (std.algebra free_monoid_scalar_templates: String -> List, one scalar +// per character) applied to the value and typed as the destination. That is the one declared route +// between the two representations -- the Unicode scalar sequence of the text -- so every target +// realizes it through its own row for `chars` and the emitter never decides it. The reverse +// direction has no declared route and is not unfolded: a code-point sequence at a host-text formal +// stays a refusal (v1.compiler.infer_types node_type_equals_core, builtin_text_argument_crossing_diags). +// The value's representation is read from the SAME peeled type direct_call_arg_mismatch_diags +// judges (expression_value_type through peel_nominal_alias_identity), so a qualified +// v2.std.text.String value whose raw resolved node still reads as the spelling String is not +// mistaken for host text and unfolded twice. +fn host_text_unfolded_at_code_point_boundary(value: Node, destination_type: Node, scope: InferScope) -> Node { + match value.inferred { + Present { value: Resolved { node: _ } } => + match argument_text_representation(value: value, type_env: scope.type_env, module_name: scope.module_name) { + HostText => + if destination_declares_scalar_sequence_unfold(destination_type: destination_type, scope: scope) { + make_named_expr_node(occurrence_identity: OccurrenceSynthetic, + name: "chars", expr_data: ExprMethodCall { method_semantics: Present { value: PlainMethodSemantics } }, + children: [value], + inferred: Present { value: Resolved { node: destination_type } }, + span: value.span, + name_span: elaborated_span(name: "chars") + ) + } else { value } + CodePointSequence => value + NotText => value + } + _ => value + } +} + +// A FORMAL IS A CODE-POINT SEQUENCE WHEN EITHER OF ITS DECLARATION-BOUND VIEWS SAYS SO, and the +// view that says so is the one the argument is typed against. The declaration-bound conformance of a +// parameter declared with the QUALIFIED v2.std.text.String is peeled to the FreeMonoid coproduct +// declaration itself, which has lost its Char argument; its substitution basis still carries +// FreeMonoid, and where it too is generalized the declared type still does. Reading only the +// first let a host value reach a qualified text parameter with no unfold. Absent means neither view is a code-point sequence. +fn formal_code_point_view(formal: ResolvedFormal, source_indices: Map) -> Node? { + match text_representation_of_type(n: formal.declaration_bound_conformance, source_indices: source_indices) { + CodePointSequence => Present { value: formal.declaration_bound_conformance } + _ => match text_representation_of_type(n: formal.substitution_basis, source_indices: source_indices) { + CodePointSequence => Present { value: formal.substitution_basis } + _ => match text_representation_of_type(n: formal.declared_type, source_indices: source_indices) { + CodePointSequence => Present { value: formal.declared_type } + _ => none + } + } + } +} + fn infer_call_arguments_generic_pass(call_args: List, value_params: List, formals: List, generic_names: List, init_subst: Map, scope: InferScope) -> ArgGenericFoldState { fold(call_args |> enumerate, init: ArgGenericFoldState { results: [], subst: init_subst }, f: (st, pair) => let a = pair.second @@ -6779,13 +6953,29 @@ fn infer_call_arguments_generic_pass(call_args: List, value_params: List type_variable_node(id: "callable_param") } - let contextual_expected = match arg_value(n: a).expr_data { + let formal_declaration_bound = match formal_selection { + CallArgumentFormalSelected { formal_index: formal_index, formal: _ } => match formals |> skip(formal_index) |> first { + Present { value: carried } => formal_code_point_view(formal: carried, source_indices: scope.type_env.source_indices) + Absent => none + } + CallArgumentFormalUnavailable => none + } + let formal_is_code_point_sequence = formal_declaration_bound != none + let code_point_formal = match formal_declaration_bound { Present { value: bound } => bound Absent => formal_conformance_type } + let argument_is_literal = match arg_value(n: a).expr_data { + ExprLiteral { value: _ } => true + _ => false + } + let contextual_expected = if formal_is_code_point_sequence && argument_is_literal { code_point_formal } else { match arg_value(n: a).expr_data { ExprVar { binding_kind: _ } => if expression_is_uppercase_constructor_reference(n: arg_value(n: a), source_indices: scope.type_env.source_indices) { formal_conformance_type } else { formal_param_type } ExprRecordLit { parent_enum: _ } => formal_param_type _ => formal_param_type - } + } } let expected = if has_formal { Present { value: contextual_expected } } else { none } - let ar = infer_expr(texpr: arg_value(n: a), scope: scope, expected: expected) + let ar_inferred = infer_expr(texpr: arg_value(n: a), scope: scope, expected: expected) + let ar = if formal_is_code_point_sequence { + InferResult { typed: host_text_unfolded_at_code_point_boundary(value: ar_inferred.typed, destination_type: code_point_formal, scope: scope), diagnostics: ar_inferred.diagnostics } + } else { ar_inferred } let next_subst = if is_lambda_expr(e: arg_value(n: a)) || !has_formal { st.subst } else if should_unify_record_lit_generics(formal: formal_raw, arg_expr: arg_value(n: a), generic_names: generic_names, source_indices: scope.type_env.source_indices) { @@ -6987,6 +7177,18 @@ type LiteralBoundary | LiteralBoundaryElaborated { elaboration: LiteralElaboration, destination_type: Node } | LiteralBoundaryRefused { message: String } +// A CODE-POINT-SEQUENCE DESTINATION IS KEYED BY ITS STRUCTURE. v1.compiler.coercion +// text_representation_of_type establishes that the destination is a corpus-declared FreeMonoid over +// Char however it is spelled -- the qualified v2.std.text.String peels to std.algebra FreeMonoid, but +// the same declaration read in its own module (the declaration-bound formal a foreign call site now +// uses) keeps its alias name String, and its declaration reference names the alias rather than +// the structure. The literal homomorphism row is keyed on the structure that survives resolution +// (gunbc.structural_realization_bindings literal_homomorphism_rows), so the lookup is asked for that +// structure directly rather than for whichever alias spelled it. +fn code_point_sequence_structure_ref() -> DeclarationRef { + decl_ref(module_path: "std.algebra", decl_name: "FreeMonoid") +} + fn literal_boundary_elaboration(lit: LiteralValue, expected: Node?, scope: InferScope) -> LiteralBoundary { match expected { Absent => LiteralBoundaryPlain @@ -6994,17 +7196,29 @@ fn literal_boundary_elaboration(lit: LiteralValue, expected: Node?, scope: Infer if exp.return_cardinality == CardOptional { LiteralBoundaryPlain } else { - match type_reference_declaration_ref(n: exp, source_indices: scope.type_env.source_indices, env: scope.type_env) { + let is_code_point_sequence = match text_representation_of_type(n: exp, source_indices: scope.type_env.source_indices) { + CodePointSequence => true + HostText => false + NotText => false + } + let identified = if is_code_point_sequence { + Present { value: code_point_sequence_structure_ref() } + } else { + type_reference_declaration_ref(n: exp, source_indices: scope.type_env.source_indices, env: scope.type_env) + } + match identified { Absent => LiteralBoundaryPlain Present { value: destination } => let kind = literal_source_kind_of(value: lit) - let element = if exp.children |> count == 1 { + let element = if is_code_point_sequence { + Present { value: decl_ref(module_path: "std.types", decl_name: "Char") } + } else if exp.children |> count == 1 { match exp.children |> first { Present { value: arg } => type_reference_declaration_ref(n: arg, source_indices: scope.type_env.source_indices, env: scope.type_env) Absent => none } } else { none } - let natively = provenance_realizes_natively(p: declaration_provenance_of_ref(d: destination, env: scope.type_env)) + let natively = if is_code_point_sequence { false } else { provenance_realizes_natively(p: declaration_provenance_of_ref(d: destination, env: scope.type_env)) } match elaborate_literal_at(rows: literal_homomorphism_rows, source_kind: kind, destination: destination, element: element, destination_realizes_natively: natively) { DirectLiteral => LiteralBoundaryPlain ViaHomomorphism { homomorphism: h } => @@ -7941,7 +8155,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu span: span, name_span: node_name_span(n: texpr) ), - diagnostics: concat(arg_diags, tier2b.kernel_diags) + diagnostics: concat(concat(arg_diags, tier2b.kernel_diags), builtin_text_argument_crossing_diags(func_name: func_name, typed_args: typed_args, scope: scope)) } } else { let callable_local = match call_target_local_binding(outcome: call_target) { diff --git a/src/v1/04_method.dag b/src/v1/04_method.dag index 7a00eed97dc..1c0f977f0b5 100644 --- a/src/v1/04_method.dag +++ b/src/v1/04_method.dag @@ -9,7 +9,8 @@ import std.algebra { AlgebraFieldTemplate, algebra_templates_for_profile, AlgebraTypeTemplate, ReceiverSelf, ReceiverElement, ReceiverKey, ReceiverValue, NamedTemplate, ContainerOf, ContainerSource, SameAsReceiver, Named, - OptionalOf, WitnessOf, TupleOf, CallableOf, AlgebraTypeVariable + OptionalOf, WitnessOf, TupleOf, CallableOf, AlgebraTypeVariable, + free_monoid_scalar_templates } import v1.std.core { Unmarked, @@ -446,6 +447,34 @@ fn builtin_signature(name: String) -> BuiltinSignature? { map_get(builtin_function_registry, name) } +// WHICH PARAMETERS OF A BUILTIN ARE HOST TEXT. A builtin's contract is realized over host text +// wherever its signature names the kernel String, and also at ReceiverSelf when the name is a +// method of the scalar-string algebra profile (std.algebra free_monoid_scalar_templates: +// substring is declared there as (Self, Int, Int) -> Self). v1.compiler.infer reads this to refuse +// a code-point-sequence value handed to such a parameter (gunbc.rung_drop +// text_boundary_identity_wall): the builtin registry otherwise admits its arguments untyped. +fn builtin_param_names(name: String) -> List { + match builtin_signature(name: name) { + Present { value: sig } => sig.params |> map(p => p.name) + Absent => [] + } +} + +fn builtin_host_text_param_names(name: String) -> List { + match builtin_signature(name: name) { + Absent => [] + Present { value: sig } => + let scalar_string_method = free_monoid_scalar_templates() |> any(t => t.name == name) + sig.params |> filter(p => + match p.ty { + NamedTemplate { name: n } => n == "String" + ReceiverSelf => scalar_string_method + _ => false + } + ) |> map(p => p.name) + } +} + fn infer_builtin_call_type(name: String) -> Node? { match builtin_signature(name: name) { Present { value: sig } => Present { value: sig.returns } diff --git a/src/v1/04_types.dag b/src/v1/04_types.dag index d718df49391..c53b3e9f066 100644 --- a/src/v1/04_types.dag +++ b/src/v1/04_types.dag @@ -20,6 +20,8 @@ import std.algebra { import std.syntax { AlgAdd, AlgebraFieldKind, BinOp, LitStr, LiteralValue } +import v1.compiler.coercion { text_representations_cross } + import std.coercion { SubtractionRefinement, SubtractionStaysInOperandAlgebra, SubtractionEscapesTo, SubtractionRefinementAmbiguous, dag_subtraction_refinement } import v1.std.core { Unmarked, @@ -932,6 +934,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map FreeMonoid -// identity is GROUNDED (std.coercion grounded_primitive_coproduct_cast_note, operator Ruling 3, -// 2026-07-15) and the v1 realization of both sides is the host string. A general-relation wall -// here is therefore a fabricated refusal (DESIGN section 5 forbids it in exactly the way it -// forbids a fabricated success). The self-host E0308 defect this wall aimed at is repaired at -// its root instead: a kernel string LITERAL at an exact v2.std.text.String boundary is -// introduced through the declared homomorphism (std.literal_elaboration, v1.compiler.infer -// literal_boundary_elaboration) before any comparison runs. The residue -- a NON-literal -// kernel-String value at an exact structural-text boundary -- stays ACCEPTED by this relation -// today, and its refusal is blocked on the peeling declared-boundary conformance capability that -// declared_type_conformance_note (v1.compiler.infer) names as its own dissolve-on: a judgment -// scoped to declared boundaries can carry the identity discrimination; the corpus-wide equality -// relation cannot. +// TEXT REPRESENTATIONS ARE COMPARED BY WHAT THEY DENOTE, NOT BY SPELLING (XL-0T ruling B; +// gunbc.rung_drop text_boundary_identity_wall). Kernel String is host text and a corpus-declared +// FreeMonoid is a code-point sequence; v1.compiler.coercion text_representations_cross asks +// the one authority the Rust renderer also reads, so node_type_equals_core refuses the pairing +// before any leaf-name arm can match `String` against `String`. A kernel LITERAL never reaches this +// point at a code-point-sequence boundary -- v1.compiler.infer literal_boundary_elaboration has +// already replaced it with its unfold image, typed as the destination -- so what this refuses is +// exactly the NON-literal crossing, which has no declared route. The wall was once landed in these +// arms and withdrawn because dag/gunbc/roadmap/roadmap_page.dag script_src_attr joined a +// std.string_type String field against a kernel "" and refused. That join was a real crossing +// hidden by a nickname: std.markup meant host text and imported the structural declaration. The +// import is deleted, the join is kernel against kernel, and nothing here exempts it. // A CALLABLE SIGNATURE IS ITS ARITY, ITS PARAMETER TYPES AND ITS CODOMAIN -- one relation over // them, parameterised by the component relation, so equality and compatibility cannot drift apart. // The signature lives in params and inferred, never in children, which is why an Arrow must be @@ -1099,7 +1094,9 @@ fn callable_signatures_agree(left: Node, right: Node, agree: fn(Node, Node) -> B } fn node_type_equals_core(left: Node, right: Node, source_indices: Map) -> Bool { - if left.connective == Arrow || (right.connective == Arrow) { + if text_representations_cross(left: left, right: right, source_indices: source_indices) { + false + } else if left.connective == Arrow || (right.connective == Arrow) { left.connective == right.connective && callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices)) } else { diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 894a366b382..0714d3d2a94 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -148,11 +148,11 @@ import v1.compiler.coercion { coerce_primitive_type, is_copy, target_callable, rust_lookup_exact_binding, declaration_realizes_natively_on_rust, type_reference_realization, declaration_realization, type_realization_decision, realized_checkpoint, realization_host_numeric_spelling, - realization_is_host_numeric, provenance_declares_structurally + realization_is_host_numeric, provenance_declares_structurally, text_representation_of_type } import std.coercion { TypeDeclarationProvenance, CorpusDeclared, KernelMinted, DeclarationIdentityAbsent, - TypeRealizationDecision + TypeRealizationDecision, HostText, CodePointSequence, NotText } import v1.compiler.dag_collect_support { connective_name } import v1.compiler.trait_derive_emit { @@ -901,25 +901,16 @@ fn rust_seed_host_container_base(name: String) -> String? { } } -fn rust_host_text_carrier_elem_name(n: Node, source_indices: Map) -> String { - match n.children |> first { - Present { value: ch } => authored_name_at(source_indices: source_indices, node: ch) - Absent => - match find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) { - Present { value: applied } => - match applied.children |> first { - Present { value: ach } => authored_name_at(source_indices: source_indices, node: ach) - Absent => "" - } - Absent => "" - } - } -} // An arrow is never a text carrier, whatever its authored name resolves to: `fn(String) -> String` // answered true here (its name reads as its return leaf) ahead of every Arrow arm, so the type // argument rendered `String` (E0277 x2 at v2.std.compilers.target_model). // +// THE ANSWER IS v1.compiler.coercion text_representation_of_type, the one authority the type +// checker's compatibility relation also reads, so a pairing the checker admits is one this renderer +// realizes with one carrier (gunbc.rung_drop text_boundary_identity_wall). The history below is why +// that authority keys as it does. +// // The String arm is keyed on the resolved declaration's PROVENANCE, never on the spelling alone // (DESIGN section 3, and gunbc.recurring_failure_mode // alias_resolution_collides_with_kernel_spelling): v2.std.text declares `type String = @@ -948,21 +939,10 @@ fn rust_host_text_carrier_elem_name(n: Node, source_indices: Map) -> Bool { if n.connective == Arrow { return false } - let nm = authored_name_at(source_indices: source_indices, node: n) - if nm == "String" { - !provenance_declares_structurally(dag_name: "String", p: type_reference_provenance(n: n)) - } else if nm == "FreeMonoid" || nm == "List" { - if rust_host_text_carrier_elem_name(n: n, source_indices: source_indices) != "Char" { - false - } else { - match type_reference_provenance(n: n) { - CorpusDeclared { decl_file: _ } => false - KernelMinted { minted_name: _ } => true - DeclarationIdentityAbsent => true - } - } - } else { - false + match text_representation_of_type(n: n, source_indices: source_indices) { + HostText => true + CodePointSequence => false + NotText => false } } diff --git a/src/v1/coercion.dag b/src/v1/coercion.dag index 7b6f864042f..9a053a23397 100644 --- a/src/v1/coercion.dag +++ b/src/v1/coercion.dag @@ -2,7 +2,7 @@ module v1.compiler.coercion import v1.compiler.artifact { RenderTarget, Rust, Python, Go, Dag } -import v1.std.core { qualified_last_segment, Node, Resolved, declaration_provenance_of, type_reference_provenance } +import v1.std.core { qualified_last_segment, Node, Resolved, declaration_provenance_of, type_reference_provenance, NewlineIndex, authored_name_at, find_property } import std.coercion { TypeCheckpoint, InhabitantDecl, CallableRepr, CastSyntax, TypeRealizationDecision, @@ -10,7 +10,8 @@ import std.coercion { ExactSourceIdentityAbsent, BareRowMigratedToExactBinding, BoundRepresentationHasNoRealizationRow, ExactBindingAmbiguousForOneDeclaration, RealizationGround, GroundedByCheckpointRow, GroundedByHostNumericAlias, GroundedByUnidentifiedBareRow, TypeDeclarationProvenance, - CorpusDeclared, KernelMinted, DeclarationIdentityAbsent + CorpusDeclared, KernelMinted, DeclarationIdentityAbsent, + TextRepresentation, HostText, CodePointSequence, NotText } import std.decl_ref { DeclarationRef } @@ -135,7 +136,7 @@ data type_reference_identity_note: String = "A realization decision is made at R fn structural_declaration_modules_for(dag_name: String) -> List { match dag_name { "Hash" => ["src/v2/std/node.dag"] - "String" => ["src/v2/std/text.dag", "dag/std/string_type.dag"] + "String" => ["src/v2/std/text.dag"] "Bool" => ["src/v2/std/logic.dag"] _ => [] } @@ -182,6 +183,93 @@ fn provenance_declares_structurally(dag_name: String, p: TypeDeclarationProvenan } } +// WHICH TEXT REPRESENTATION A TYPE NODE DENOTES (std.coercion TextRepresentation). ONE authority, +// read by the type-compatibility relation (v1.compiler.infer_types node_type_equals_core) and by the +// Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type), so the checker cannot admit a +// pairing the emitted program then refuses. +// +// Keyed on the resolved declaration's provenance and its element, never on the spelling alone. A +// corpus-declared text carrier over Char is the code-point sequence WHATEVER it is spelled: a +// resolved `String` reference (a local `type String = FreeMonoid` keeps the alias name +// and carries the Char element), a peeled `FreeMonoid` -- read from the node's resolved name as +// well as its authored spelling, since a local `type Text = FreeMonoid` resolves to a +// FreeMonoid node that keeps the authored spelling Text -- or a List. The +// spelling String with no element is structural only where its declaration is on the structural +// roster (structural_declaration_modules_for); an undotted kernel mint, or a reference whose +// declaration is absent, is host text -- the same answer the renderer has always given those nodes. +fn text_carrier_element_name(n: Node, source_indices: Map) -> String { + match n.children |> first { + Present { value: ch } => authored_name_at(source_indices: source_indices, node: ch) + Absent => + match find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) { + Present { value: applied } => + match applied.children |> first { + Present { value: ach } => authored_name_at(source_indices: source_indices, node: ach) + Absent => "" + } + Absent => "" + } + } +} + +// A dotted kernel mint is not the kernel: the resolver mints a stand-in on the reference span for a +// QUALIFIED structural reference (v2.std.text.String arrives as a kernel-minted +// std.algebra.FreeMonoid), so it names the corpus declaration exactly as CorpusDeclared does. +// Only an undotted mint -- the kernel's own String, or a bare List the seed mints -- is host. +fn provenance_is_corpus_declared(p: TypeDeclarationProvenance) -> Bool { + match p { + CorpusDeclared { decl_file: _ } => true + KernelMinted { minted_name: m } => contains(m, ".") + DeclarationIdentityAbsent => false + } +} + +// A LOCAL ALIAS OF A QUALIFIED TEXT REFERENCE (`type LocalText = v2.std.text.String`) resolves to a +// childless leaf that keeps its authored spelling and carries the qualified target only as its +// name. That target is a code-point sequence exactly when its module is on the structural roster. +// The roster is keyed by file path, so the module path is mapped onto that path here -- the same +// positional residue structural_declaration_modules_for already declares, not a second authority. +fn qualified_string_names_structural_declaration(qualified: String) -> Bool { + let module_path = substring(s: qualified, start: 0, end: string_length(s: qualified) - string_length(s: ".String")) + let module_file = concat(replace(module_path, ".", "/"), ".dag") + structural_declaration_modules_for(dag_name: "String") |> any(f => contains(f, module_file)) +} + +fn text_representation_of_type(n: Node, source_indices: Map) -> TextRepresentation { + let authored = qualified_last_segment(name: authored_name_at(source_indices: source_indices, node: n)) + let resolved_name = qualified_last_segment(name: n.name) + let element = qualified_last_segment(name: text_carrier_element_name(n: n, source_indices: source_indices)) + let p = type_reference_provenance(n: n) + let is_monoid = authored == "FreeMonoid" || authored == "List" || resolved_name == "FreeMonoid" || resolved_name == "List" + if is_monoid { + if element != "Char" { NotText } + else if provenance_is_corpus_declared(p: p) { CodePointSequence } + else { HostText } + } else if resolved_name == "String" && authored != "String" && contains(n.name, ".") { + if qualified_string_names_structural_declaration(qualified: n.name) { CodePointSequence } else { NotText } + } else if authored == "String" { + if element == "Char" && provenance_is_corpus_declared(p: p) { CodePointSequence } + else if element != "" { NotText } + else if provenance_declares_structurally(dag_name: "String", p: p) { CodePointSequence } + else { HostText } + } else { + NotText + } +} + +// A CROSSING between the two representations: both sides are text and they disagree. Every such +// pairing is refused by the compatibility relation; the literal half never reaches it, because a +// kernel literal at a code-point-sequence boundary is elaborated through the unfold first. +fn text_representations_cross(left: Node, right: Node, source_indices: Map) -> Bool { + let l = text_representation_of_type(n: left, source_indices: source_indices) + let r = text_representation_of_type(n: right, source_indices: source_indices) + match l { + NotText => false + HostText => match r { CodePointSequence => true HostText => false NotText => false } + CodePointSequence => match r { HostText => true CodePointSequence => false NotText => false } + } +} + data numeric_realization_identity_note: String = "Realization is keyed on the DECLARING MODULE of the type, never on the authored spelling alone. Two declarations may share a spelling -- std.nat.Nat is CommutativeSemiring and realizes natively, while v2.std.nat.Nat is the Peano coproduct Zero|Succ and must NOT -- so a bare-name rule realizes the wrong one. decl_file is the resolved declaration's ident_span file; the empty string means identity is UNKNOWN at this site, which yields NO realization (render structurally) rather than a guess. This replaces the deleted rust_corpus_repr closure-provenance switch: what a declaration realizes as is a fact about that declaration and its target, never about which other sources happen to share the closure. RELOCATED here from v1.compiler.emit_rust (smart-ram-730, adhoc-2ea6fb98-a3f, 2026-08-21, review 54335) alongside numeric_realization_roster_extension_note, numeric_realization_declaring_modules, decl_file_realizes_natively and rust_seed_host_numeric_alias -- completing the relocation structural_declaration_modules_for's own precedent already established for the negative-form (structural) half of this two-authority shape; emit_rust now imports rust_seed_host_numeric_alias back from here exactly as it already imports lookup_checkpoint, one direction, no cycle. See v1.compiler.emit_rust numeric_realization_relocation_note for the fuller account." data numeric_realization_roster_extension_note: String = "THIS ROSTER IS A LIVE ENUMERATION AND A ROW MAY ONLY BE ADDED FOR A DECLARATION WHOSE NATIVE REALIZATION IS ALREADY TRUE, NEVER TO MAKE A FAILING SITE COMPILE. A module belongs here when the types it declares ARE the host numeric type at the target -- dag/std/nat.dag and dag/std/integer.dag are the two grounded numeric authorities, and the bool { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct HostText; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct CodePointSequence; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct NotText; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct NoResolutionBoundAtReference; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/src/v1/stage0/src/v1_compiler_coercion.rs b/src/v1/stage0/src/v1_compiler_coercion.rs index d7c3bb2202f..16774de2ebc 100644 --- a/src/v1/stage0/src/v1_compiler_coercion.rs +++ b/src/v1/stage0/src/v1_compiler_coercion.rs @@ -28,13 +28,14 @@ use crate::std_coercion::RealizationRefusalCause::{ DeclarationIdentityUnavailable, ExactBindingAmbiguousForOneDeclaration, ExactSourceIdentityAbsent, }; +use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; use crate::std_coercion::TypeRealizationDecision::{RealizationRefused, Realized, Unrealized}; pub use crate::std_coercion::{ CallableRepr, CastSyntax, InhabitantDecl, RealizationGround, RealizationRefusalCause, - TypeCheckpoint, TypeDeclarationProvenance, TypeRealizationDecision, + TextRepresentation, TypeCheckpoint, TypeDeclarationProvenance, TypeRealizationDecision, }; pub use crate::std_decl_ref::DeclarationRef; pub use crate::std_target_representation::ExactBindingResolution; @@ -52,9 +53,10 @@ use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::InferredNode::Resolved; pub use crate::v1_std_core::{ - declaration_provenance_of, qualified_last_segment, type_reference_provenance, + authored_name_at, declaration_provenance_of, find_property, qualified_last_segment, + type_reference_provenance, }; -pub use crate::v1_std_core::{InferredNode, Node}; +pub use crate::v1_std_core::{InferredNode, NewlineIndex, Node}; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; use im::{vector as vec, HashMap, OrdSet as BTreeSet, Vector as Vec}; @@ -161,10 +163,7 @@ pub fn type_reference_identity_note() -> String { pub fn structural_declaration_modules_for(dag_name: String) -> Rc> { match dag_name.clone().as_str() { "Hash" => Rc::new(vec!["src/v2/std/node.dag".to_string()]), - "String" => Rc::new(vec![ - "src/v2/std/text.dag".to_string(), - "dag/std/string_type.dag".to_string(), - ]), + "String" => Rc::new(vec!["src/v2/std/text.dag".to_string()]), "Bool" => Rc::new(vec!["src/v2/std/logic.dag".to_string()]), _ => Rc::new(vec![]), } @@ -202,6 +201,155 @@ pub fn provenance_declares_structurally( } } +pub fn text_carrier_element_name( + n: Rc, + source_indices: Rc>>, +) -> String { + match n.children.clone().first().cloned() { + Some(ch) => crate::v1_std_core::authored_name_at(source_indices.clone(), ch.clone()), + std::option::Option::None => match crate::v1_std_core::find_property( + n.properties.clone(), + "__applied_type_args".to_string(), + source_indices.clone(), + ) { + Some(applied) => match applied.children.clone().first().cloned() { + Some(ach) => { + crate::v1_std_core::authored_name_at(source_indices.clone(), ach.clone()) + } + std::option::Option::None => "".to_string(), + }, + std::option::Option::None => "".to_string(), + }, + } +} + +pub fn provenance_is_corpus_declared(p: Rc) -> bool { + match (*p.clone()).clone() { + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => true, + TypeDeclarationProvenance::KernelMinted { minted_name: m, .. } => { + v1_rt::contains(m.clone(), ".".to_string()) + } + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + } +} + +pub fn qualified_string_names_structural_declaration(qualified: String) -> bool { + { + let module_path = v1_rt::substring( + &qualified, + 0, + v1_rt::int_sub( + v1_rt::string_length(&qualified), + v1_rt::string_length(&".String".to_string()), + ), + ); + let module_file = v1_rt::concat( + v1_rt::replace(module_path.clone(), ".".to_string(), "/".to_string()), + ".dag".to_string(), + ); + { + let mut __found = false; + for f in structural_declaration_modules_for("String".to_string()) + .iter() + .cloned() + { + if v1_rt::contains(f.clone(), module_file.clone()) { + __found = true; + break; + } + } + __found + } + } +} + +pub fn text_representation_of_type( + n: Rc, + source_indices: Rc>>, +) -> TextRepresentation { + { + let authored = crate::v1_std_core::qualified_last_segment( + crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()), + ); + let resolved_name = crate::v1_std_core::qualified_last_segment(n.name.clone()); + let element = crate::v1_std_core::qualified_last_segment(text_carrier_element_name( + n.clone(), + source_indices.clone(), + )); + let p = crate::v1_std_core::type_reference_provenance(n.clone()); + let is_monoid = ((((authored.clone() == "FreeMonoid".to_string()) + || (authored.clone() == "List".to_string())) + || (resolved_name.clone() == "FreeMonoid".to_string())) + || (resolved_name.clone() == "List".to_string())); + if is_monoid.clone() { + if (element.clone() != "Char".to_string()) { + TextRepresentation::NotText + } else { + if provenance_is_corpus_declared(p.clone()) { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::HostText + } + } + } else { + if (((resolved_name.clone() == "String".to_string()) + && (authored.clone() != "String".to_string())) + && v1_rt::contains(n.name.clone(), ".".to_string())) + { + if qualified_string_names_structural_declaration(n.name.clone()) { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::NotText + } + } else { + if (authored.clone() == "String".to_string()) { + if ((element.clone() == "Char".to_string()) + && provenance_is_corpus_declared(p.clone())) + { + TextRepresentation::CodePointSequence + } else { + if (element.clone() != "".to_string()) { + TextRepresentation::NotText + } else { + if provenance_declares_structurally("String".to_string(), p.clone()) { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::HostText + } + } + } + } else { + TextRepresentation::NotText + } + } + } + } +} + +pub fn text_representations_cross( + left: Rc, + right: Rc, + source_indices: Rc>>, +) -> bool { + { + let l = text_representation_of_type(left.clone(), source_indices.clone()); + let r = text_representation_of_type(right.clone(), source_indices.clone()); + match l.clone() { + TextRepresentation::NotText => false, + TextRepresentation::HostText => match r.clone() { + TextRepresentation::CodePointSequence => true, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::CodePointSequence => match r.clone() { + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + }, + } + } +} + pub fn numeric_realization_identity_note() -> String { thread_local! { static CACHED: String = { diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 38958f58736..ce988c66874 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -68,11 +68,14 @@ pub use crate::gunbc_structural_realization_bindings::{ }; pub use crate::std_algebra::AlgebraFieldTemplate; pub use crate::std_algebra::{is_collection_filter_template, trim}; +use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; use crate::std_coercion::TypeRealizationDecision::*; -pub use crate::std_coercion::{TypeDeclarationProvenance, TypeRealizationDecision}; +pub use crate::std_coercion::{ + TextRepresentation, TypeDeclarationProvenance, TypeRealizationDecision, +}; pub use crate::std_decl_ref::decl_ref; use crate::std_decl_ref::DeclField::WholeDeclaration; pub use crate::std_decl_ref::{DeclField, DeclarationRef}; @@ -132,7 +135,7 @@ pub use crate::v1_compiler_coercion::{ coerce_primitive_type, declaration_realization, declaration_realizes_natively_on_rust, is_copy, provenance_declares_structurally, realization_host_numeric_spelling, realization_is_host_numeric, realized_checkpoint, rust_lookup_exact_binding, target_callable, - type_realization_decision, type_reference_realization, + text_representation_of_type, type_realization_decision, type_reference_realization, }; pub use crate::v1_compiler_compiler_tests_rust::compiler_tests_source; pub use crate::v1_compiler_dag_collect_support::connective_name; @@ -1345,28 +1348,6 @@ pub fn rust_seed_host_container_base(name: String) -> Option { } } -pub fn rust_host_text_carrier_elem_name( - n: Rc, - source_indices: Rc>>, -) -> String { - match n.children.clone().first().cloned() { - Some(ch) => crate::v1_std_core::authored_name_at(source_indices.clone(), ch.clone()), - std::option::Option::None => match crate::v1_std_core::find_property( - n.properties.clone(), - "__applied_type_args".to_string(), - source_indices.clone(), - ) { - Some(applied) => match applied.children.clone().first().cloned() { - Some(ach) => { - crate::v1_std_core::authored_name_at(source_indices.clone(), ach.clone()) - } - std::option::Option::None => "".to_string(), - }, - std::option::Option::None => "".to_string(), - }, - } -} - pub fn is_host_text_carrier_type( n: Rc, source_indices: Rc>>, @@ -1375,28 +1356,13 @@ pub fn is_host_text_carrier_type( if (n.connective.clone() == Connective::Arrow) { return false; } - let nm = crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()); - if (nm.clone() == "String".to_string()) { - !crate::v1_compiler_coercion::provenance_declares_structurally( - "String".to_string(), - crate::v1_std_core::type_reference_provenance(n.clone()), - ) - } else { - if ((nm.clone() == "FreeMonoid".to_string()) || (nm.clone() == "List".to_string())) { - if (rust_host_text_carrier_elem_name(n.clone(), source_indices.clone()) - != "Char".to_string()) - { - false - } else { - match (*crate::v1_std_core::type_reference_provenance(n.clone())).clone() { - TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, - TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, - TypeDeclarationProvenance::DeclarationIdentityAbsent => true, - } - } - } else { - false - } + match crate::v1_compiler_coercion::text_representation_of_type( + n.clone(), + source_indices.clone(), + ) { + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, } } } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 49c7983be4b..67a422cb20e 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -18,9 +18,15 @@ pub use crate::gunbc_structural_realization_bindings::literal_homomorphism_rows; pub use crate::std_algebra::carrier_container_equality_rows; use crate::std_algebra::CollectionSizeEffect::ShrinkEffect; pub use crate::std_algebra::{CollectionSizeEffect, FreeMonoid}; -pub use crate::std_coercion::SubtractionRefinement; use crate::std_coercion::SubtractionRefinement::SubtractionRefinementAmbiguous; +use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; +use crate::std_coercion::TypeDeclarationProvenance::{ + CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, +}; pub use crate::std_coercion::{dag_can_cast, dag_cast_requires_proof, is_dag_cast_domain_type}; +pub use crate::std_coercion::{ + SubtractionRefinement, TextRepresentation, TypeDeclarationProvenance, +}; pub use crate::std_computation::ShrinkFactor; use crate::std_computation::ShrinkFactor::{ConstantShrink, ProportionalShrink, UnitShrink}; use crate::std_content_hash::ContentHash::*; @@ -29,6 +35,7 @@ pub use crate::std_content_hash::{ content_hash_validate_lower_hex_length, }; pub use crate::std_content_hash::{ContentHash, Fnv1a64Structural}; +pub use crate::std_decl_ref::decl_ref; pub use crate::std_decl_ref::DeclarationRef; pub use crate::std_dissolution::DissolutionCondition; use crate::std_dissolution::DissolutionCondition::*; @@ -79,7 +86,9 @@ pub use crate::std_types::{ container_param_name, container_template_algebra, is_kernel_type, kernel_type_set, }; pub use crate::std_types::{NonEmptyStr, SourceSpan}; -pub use crate::v1_compiler_coercion::provenance_realizes_natively; +pub use crate::v1_compiler_coercion::{ + provenance_realizes_natively, text_representation_of_type, text_representations_cross, +}; pub use crate::v1_compiler_infer_access::AccessCheckResultNode; pub use crate::v1_compiler_infer_access::{check_index_access_node, check_slice_access_node}; pub use crate::v1_compiler_infer_cycle::detect_type_cycles_kahn; @@ -151,7 +160,8 @@ pub use crate::v1_compiler_infer_lookup::{ ExactDeclarationIdentity, KnownMethodResolution, }; pub use crate::v1_compiler_infer_method::{ - builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, + builtin_host_text_param_names, builtin_kernel_seed_diagnostics, builtin_param_names, + infer_builtin_call_type, resolve_builtin_call_type, }; use crate::v1_compiler_infer_patterns::PatternSubject::*; pub use crate::v1_compiler_infer_patterns::{ @@ -1952,10 +1962,10 @@ pub fn declared_type_conformance_diags( let si = scope.type_env.clone().source_indices.clone(); let both_ground = (conformance_ground_type(declared.clone(), si.clone()) && conformance_ground_type(produced.clone(), si.clone())); - if declared_type_kernel_inhabitance_mismatch_here_or_at_element( + if crate::v1_compiler_coercion::text_representations_cross( declared.clone(), produced.clone(), - scope.clone(), + si.clone(), ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape(declared.clone(), si.clone()), @@ -1964,12 +1974,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if (nominal_product_inhabitance_refusal( + if declared_type_kernel_inhabitance_mismatch_here_or_at_element( declared.clone(), produced.clone(), scope.clone(), - ) != std::option::Option::None) - { + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape(declared.clone(), si.clone()), crate::v1_compiler_infer_types::node_type_shape(produced.clone(), si.clone()), @@ -1977,11 +1986,12 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if coproduct_payload_where_parent_required( + if (nominal_product_inhabitance_refusal( declared.clone(), produced.clone(), scope.clone(), - ) { + ) != std::option::Option::None) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -1995,14 +2005,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if ((type_node_is_arrow(declared.clone()) - && type_node_is_arrow(produced.clone())) - && callable_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - )) - { + if coproduct_payload_where_parent_required( + declared.clone(), + produced.clone(), + scope.clone(), + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2016,11 +2023,14 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if callable_element_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if ((type_node_is_arrow(declared.clone()) + && type_node_is_arrow(produced.clone())) + && callable_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + )) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2034,28 +2044,47 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if !both_ground.clone() { - Rc::new(vec![]) + if callable_element_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + ) { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) } else { - if crate::v1_compiler_infer_types::node_type_compatible( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if !both_ground.clone() { Rc::new(vec![]) } else { - Rc::new(vec![type_mismatch_error( - crate::v1_compiler_infer_types::node_type_shape( - declared.clone(), - si.clone(), - ), - crate::v1_compiler_infer_types::node_type_shape( - produced.clone(), - si.clone(), - ), - span.clone(), - scope.module_name.clone(), - )]) + if crate::v1_compiler_infer_types::node_type_compatible( + declared.clone(), + produced.clone(), + si.clone(), + ) { + Rc::new(vec![]) + } else { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) + } } } } @@ -7658,12 +7687,16 @@ pub fn direct_call_arg_type_mismatch( { false } else { - ((nominal_call_arg_brand_mismatch( + (((crate::v1_compiler_coercion::text_representations_cross( + formal.clone(), + actual.clone(), + source_indices.clone(), + ) || nominal_call_arg_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), source_indices.clone(), - ) || container_element_nominal_brand_mismatch( + )) || container_element_nominal_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), @@ -8334,6 +8367,76 @@ pub fn direct_call_shape_wall_note() -> String { CACHED.with(|c: &String| c.clone()) } +pub fn argument_text_representation( + value: Rc, + type_env: Rc, + module_name: String, +) -> TextRepresentation { + { + let raw = expression_value_type(value.clone()); + let peeled = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity( + raw.clone(), + type_env.clone(), + module_name.clone(), + ); + let raw_rep = crate::v1_compiler_coercion::text_representation_of_type( + raw.clone(), + type_env.source_indices.clone(), + ); + let peeled_rep = crate::v1_compiler_coercion::text_representation_of_type( + peeled.clone(), + type_env.source_indices.clone(), + ); + let raw_is_kernel_mint = + match (*crate::v1_std_core::type_reference_provenance(raw.clone())).clone() { + TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + }; + match raw_rep.clone() { + TextRepresentation::CodePointSequence => TextRepresentation::CodePointSequence, + TextRepresentation::HostText => { + if raw_is_kernel_mint.clone() { + TextRepresentation::HostText + } else { + match peeled_rep.clone() { + TextRepresentation::CodePointSequence => { + TextRepresentation::CodePointSequence + } + TextRepresentation::HostText => TextRepresentation::HostText, + TextRepresentation::NotText => TextRepresentation::HostText, + } + } + } + TextRepresentation::NotText => peeled_rep.clone(), + } + } +} + +pub fn text_representation_is_text(r: TextRepresentation) -> bool { + match r.clone() { + TextRepresentation::NotText => false, + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => true, + } +} + +pub fn text_representations_disagree(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::NotText => false, + TextRepresentation::HostText => match b.clone() { + TextRepresentation::CodePointSequence => true, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + }, + } +} + pub fn direct_call_arg_mismatch_diags( plan: Rc>>, typed_args: Rc>>, @@ -8360,10 +8463,27 @@ if match (*actual_expr.expr_data.clone()).clone() { { let actual_raw = expression_value_type(actual_expr.clone()); let actual = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(actual_raw.clone(), type_env.clone(), module_name.clone()); -if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { - Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) +let formal_text = if (formal_code_point_view(app.formal.clone(), source_indices.clone()) != std::option::Option::None) { + TextRepresentation::CodePointSequence } else { - Rc::new(vec![]) + crate::v1_compiler_coercion::text_representation_of_type(formal.clone(), source_indices.clone()) + }; +let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone(), module_name.clone()); +if text_representations_disagree(formal_text.clone(), actual_text.clone()) { + Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::InternalError { + message: v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing at a call argument: declared ".to_string(), crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone())), ", produced ".to_string()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone())), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text".to_string()), + span: actual_expr.span.clone(), +}), module_name.clone())]) + } else { + if (text_representation_is_text(formal_text.clone()) && text_representation_is_text(actual_text.clone())) { + Rc::new(vec![]) + } else { + if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { + Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) + } else { + Rc::new(vec![]) + } + } } } } @@ -8385,6 +8505,67 @@ pub struct Tier2bBt { pub kernel_diags: Rc>>, } +pub fn builtin_text_argument_crossing_diags( + func_name: String, + typed_args: Rc>>, + scope: Rc, +) -> Rc>> { + { + let host_params = + crate::v1_compiler_infer_method::builtin_host_text_param_names(func_name.clone()); + if ((host_params.clone().len() as i64) == 0) { + Rc::new(vec![]) + } else { + { + let formal_names = + crate::v1_compiler_infer_method::builtin_param_names(func_name.clone()); + Rc::new({ + let mut __result = Vec::new(); + for pair in Rc::new( + typed_args + .clone() + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (i as i64, v)) + .collect::>(), + ) + .iter() + .cloned() + { + __result.extend((*{ + let arg = pair.1.clone(); +let bound_name = match crate::v1_std_core::arg_name_at(arg.clone(), scope.type_env.clone().source_indices.clone()) { + Some(label) => label.clone(), + std::option::Option::None => match formal_names.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + Some(n) => n.clone(), + std::option::Option::None => "".to_string(), +}, +}; +if !{ let mut __found = false; for n in host_params.iter().cloned() { if (n.clone() == bound_name.clone()) { __found = true; break; } } __found } { + Rc::new(vec![]) + } else { + { + let value = crate::v1_std_core::arg_value(arg.clone()); +match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation(value.clone(), scope.type_env.clone(), scope.module_name.clone()) { + TextRepresentation::CodePointSequence => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing: argument '".to_string(), bound_name.clone()), "' of ".to_string()), func_name.clone()), " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence".to_string()), value.span.clone(), scope.module_name.clone())]), + TextRepresentation::HostText => Rc::new(vec![]), + TextRepresentation::NotText => Rc::new(vec![]), +}, + _ => Rc::new(vec![]), +} +} + } +}).iter().cloned()); + } + __result + }) + } + } + } +} + pub fn infer_tier2b_builtin_with_kernel_diags( func_name: String, typed_args: Rc>>, @@ -10243,6 +10424,95 @@ pub fn expression_is_uppercase_constructor_reference( } } +pub fn destination_declares_scalar_sequence_unfold( + destination_type: Rc, + scope: Rc, +) -> bool { + match (*literal_boundary_elaboration( + Rc::new(LiteralValue::LitStr { + value: "".to_string(), + }), + Some(destination_type.clone()), + scope.clone(), + )) + .clone() + { + LiteralBoundary::LiteralBoundaryElaborated { elaboration: e, .. } => { + match (*e.homomorphism.clone().producer.clone()).clone() { + LiteralUnfolding::UnicodeScalarSequenceUnfold => true, + _ => false, + } + } + LiteralBoundary::LiteralBoundaryPlain => false, + LiteralBoundary::LiteralBoundaryRefused { message: _, .. } => false, + } +} + +pub fn host_text_unfolded_at_code_point_boundary( + value: Rc, + destination_type: Rc, + scope: Rc, +) -> Rc { + match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation( + value.clone(), + scope.type_env.clone(), + scope.module_name.clone(), + ) { + TextRepresentation::HostText => { + if destination_declares_scalar_sequence_unfold( + destination_type.clone(), + scope.clone(), + ) { + crate::v1_std_core::make_named_expr_node( + Rc::new(NodeOccurrenceIdentity::OccurrenceSynthetic), + "chars".to_string(), + Rc::new(ExprData::ExprMethodCall { + method_semantics: Some(Rc::new(MethodSemantics::PlainMethodSemantics)), + }), + Rc::new(vec![value.clone()]), + Some(Rc::new(InferredNode::Resolved { + node: destination_type.clone(), + })), + value.span.clone(), + elaborated_span("chars".to_string()), + ) + } else { + value.clone() + } + } + TextRepresentation::CodePointSequence => value.clone(), + TextRepresentation::NotText => value.clone(), + }, + _ => value.clone(), + } +} + +pub fn formal_code_point_view( + formal: Rc, + source_indices: Rc>>, +) -> Option> { + match crate::v1_compiler_coercion::text_representation_of_type( + formal.declaration_bound_conformance.clone(), + source_indices.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declaration_bound_conformance.clone()), + _ => match crate::v1_compiler_coercion::text_representation_of_type( + formal.substitution_basis.clone(), + source_indices.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.substitution_basis.clone()), + _ => match crate::v1_compiler_coercion::text_representation_of_type( + formal.declared_type.clone(), + source_indices.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declared_type.clone()), + _ => std::option::Option::None, + }, + }, + } +} + pub fn infer_call_arguments_generic_pass( call_args: Rc>>, value_params: Rc>>, @@ -10331,33 +10601,80 @@ pub fn infer_call_arguments_generic_pass( type_variable_node("callable_param".to_string()) } }; - let contextual_expected = + let formal_declaration_bound = match (*formal_selection.clone()).clone() { + CallArgumentFormalSelection::CallArgumentFormalSelected { + formal_index, .. + } => match formals + .clone() + .iter() + .cloned() + .skip(formal_index.clone() as usize) + .next() + { + Some(carried) => formal_code_point_view( + carried.clone(), + scope.type_env.clone().source_indices.clone(), + ), + std::option::Option::None => std::option::Option::None, + }, + CallArgumentFormalSelection::CallArgumentFormalUnavailable => { + std::option::Option::None + } + }; + let formal_is_code_point_sequence = + (formal_declaration_bound.clone() != std::option::Option::None); + let code_point_formal = match formal_declaration_bound.clone() { + Some(bound) => bound.clone(), + std::option::Option::None => formal_conformance_type.clone(), + }; + let argument_is_literal = match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { - ExprData::ExprVar { - binding_kind: _, .. - } => { - if expression_is_uppercase_constructor_reference( - crate::v1_std_core::arg_value(a.clone()), - scope.type_env.clone().source_indices.clone(), - ) { - formal_conformance_type.clone() - } else { - formal_param_type.clone() + ExprData::ExprLiteral { value: _, .. } => true, + _ => false, + }; + let contextual_expected = + if (formal_is_code_point_sequence.clone() && argument_is_literal.clone()) { + code_point_formal.clone() + } else { + match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { + ExprData::ExprVar { + binding_kind: _, .. + } => { + if expression_is_uppercase_constructor_reference( + crate::v1_std_core::arg_value(a.clone()), + scope.type_env.clone().source_indices.clone(), + ) { + formal_conformance_type.clone() + } else { + formal_param_type.clone() + } } + ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), + _ => formal_param_type.clone(), } - ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), - _ => formal_param_type.clone(), }; let expected = if has_formal.clone() { Some(contextual_expected.clone()) } else { std::option::Option::None }; - let ar = infer_expr( + let ar_inferred = infer_expr( crate::v1_std_core::arg_value(a.clone()), scope.clone(), expected.clone(), ); + let ar = if formal_is_code_point_sequence.clone() { + Rc::new(InferResult { + typed: host_text_unfolded_at_code_point_boundary( + ar_inferred.typed.clone(), + code_point_formal.clone(), + scope.clone(), + ), + diagnostics: ar_inferred.diagnostics.clone(), + }) + } else { + ar_inferred.clone() + }; let next_subst = if (is_lambda_expr(crate::v1_std_core::arg_value(a.clone())) || !has_formal.clone()) { @@ -10605,6 +10922,10 @@ pub enum LiteralBoundary { }, } +pub fn code_point_sequence_structure_ref() -> Rc { + crate::std_decl_ref::decl_ref("std.algebra".to_string(), "FreeMonoid".to_string()) +} + pub fn literal_boundary_elaboration( lit: Rc, expected: Option>, @@ -10616,36 +10937,56 @@ pub fn literal_boundary_elaboration( if (exp.return_cardinality.clone() == Cardinality::CardOptional) { Rc::new(LiteralBoundary::LiteralBoundaryPlain) } else { - match crate::v1_compiler_infer_env::type_reference_declaration_ref( - exp.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) { - std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), - Some(destination) => { - let kind = - crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); - let element = if ((exp.children.clone().len() as i64) == 1) { - match exp.children.clone().first().cloned() { - Some(arg) => { - crate::v1_compiler_infer_env::type_reference_declaration_ref( - arg.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) - } - std::option::Option::None => std::option::Option::None, - } - } else { - std::option::Option::None + { + let is_code_point_sequence = + match crate::v1_compiler_coercion::text_representation_of_type( + exp.clone(), + scope.type_env.clone().source_indices.clone(), + ) { + TextRepresentation::CodePointSequence => true, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, }; - let natively = crate::v1_compiler_coercion::provenance_realizes_natively( - crate::v1_compiler_infer_env::declaration_provenance_of_ref( - destination.clone(), - scope.type_env.clone(), - ), - ); - match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { + let identified = if is_code_point_sequence.clone() { + Some(code_point_sequence_structure_ref()) + } else { + crate::v1_compiler_infer_env::type_reference_declaration_ref( + exp.clone(), + scope.type_env.clone().source_indices.clone(), + scope.type_env.clone(), + ) + }; + match identified.clone() { + std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), + Some(destination) => { + let kind = + crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); + let element = if is_code_point_sequence.clone() { + Some(crate::std_decl_ref::decl_ref( + "std.types".to_string(), + "Char".to_string(), + )) + } else { + if ((exp.children.clone().len() as i64) == 1) { + match exp.children.clone().first().cloned() { + Some(arg) => crate::v1_compiler_infer_env::type_reference_declaration_ref(arg.clone(), scope.type_env.clone().source_indices.clone(), scope.type_env.clone()), + std::option::Option::None => std::option::Option::None, +} + } else { + std::option::Option::None + } + }; + let natively = if is_code_point_sequence.clone() { + false + } else { + crate::v1_compiler_coercion::provenance_realizes_natively( + crate::v1_compiler_infer_env::declaration_provenance_of_ref( + destination.clone(), + scope.type_env.clone(), + ), + ) + }; + match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { LiteralElaborationOutcome::DirectLiteral => Rc::new(LiteralBoundary::LiteralBoundaryPlain), LiteralElaborationOutcome::ViaHomomorphism { homomorphism: h, .. } => Rc::new(LiteralBoundary::LiteralBoundaryElaborated { elaboration: Rc::new(LiteralElaboration { @@ -10659,6 +11000,7 @@ pub fn literal_boundary_elaboration( message: crate::std_literal_elaboration::literal_elaboration_refusal_message(c.clone()), }), } + } } } } @@ -12247,7 +12589,7 @@ Rc::new(InferResult { }), typed_arg_nodes.clone(), Some(Rc::new(InferredNode::Resolved { node: bt.clone(), })), span.clone(), crate::v1_std_core::node_name_span(texpr.clone())), - diagnostics: v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), + diagnostics: v1_rt::concat(v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), builtin_text_argument_crossing_diags(func_name.clone(), typed_args.clone(), scope.clone())), }) } } else { diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 6d4071edaac..5553cff0c61 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -1665,6 +1665,14 @@ pub fn record_global_bare_ambiguous_silent_pick( } pub fn global_bare_lookup(env: Rc, name: String) -> Option> { + if crate::std_types::is_kernel_type(name.clone()) { + std::option::Option::None + } else { + global_bare_lookup_candidates(env.clone(), name.clone()) + } +} + +pub fn global_bare_lookup_candidates(env: Rc, name: String) -> Option> { match v1_rt::map_get(&env.symbol_index.clone().global_bare.clone(), name.clone()) .as_deref() .cloned() diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index fda6f61e115..8099b3327b8 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -7,7 +7,9 @@ use crate::std_algebra::AlgebraTypeTemplate::{ ReceiverKey, ReceiverSelf, ReceiverValue, TupleOf, WitnessOf, }; use crate::std_algebra::ContainerSource::{Named, SameAsReceiver}; -pub use crate::std_algebra::{algebra_templates_for_profile, all_algebra_profiles}; +pub use crate::std_algebra::{ + algebra_templates_for_profile, all_algebra_profiles, free_monoid_scalar_templates, +}; pub use crate::std_algebra::{ AlgebraFieldTemplate, AlgebraProfile, AlgebraTypeTemplate, ContainerSource, }; @@ -2020,6 +2022,64 @@ pub fn builtin_signature(name: String) -> Option> { v1_rt::map_get(&builtin_function_registry(), name.clone()) } +pub fn builtin_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + Some(sig) => Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + __result.push(p.name.clone()); + } + __result + }), + std::option::Option::None => Rc::new(vec![]), + } +} + +pub fn builtin_host_text_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + std::option::Option::None => Rc::new(vec![]), + Some(sig) => { + let scalar_string_method = { + let mut __found = false; + for t in crate::std_algebra::free_monoid_scalar_templates() + .iter() + .cloned() + { + if (t.name.clone() == name.clone()) { + __found = true; + break; + } + } + __found + }; + Rc::new({ + let mut __result = Vec::new(); + for p in Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + if match (*p.ty.clone()).clone() { + AlgebraTypeTemplate::NamedTemplate { name: n, .. } => { + (n.clone() == "String".to_string()) + } + AlgebraTypeTemplate::ReceiverSelf => scalar_string_method.clone(), + _ => false, + } { + __result.push(p); + } + } + __result + }) + .iter() + .cloned() + { + __result.push(p.name.clone()); + } + __result + }) + } + } +} + pub fn infer_builtin_call_type(name: String) -> Option> { match builtin_signature(name.clone()) { Some(sig) => Some(sig.returns.clone()), diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index adffe25e604..bf719a75827 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -37,6 +37,7 @@ pub use crate::std_types::{ container_expected_arity, container_param_name, container_template_algebra, container_template_alias_algebra, container_template_alias_rows, is_container_type, }; +pub use crate::v1_compiler_coercion::text_representations_cross; pub use crate::v1_compiler_infer_env::type_reference_declaration_ref; pub use crate::v1_compiler_infer_env::{TypeBinding, TypeEnv}; use crate::v1_rt; @@ -2440,162 +2441,168 @@ pub fn node_type_compatible( if (left_tv.clone() || right_tv.clone()) { true } else { - if (left_opt.clone() && right_is_unit.clone()) { - true + if crate::v1_compiler_coercion::text_representations_cross( + left.clone(), + right.clone(), + source_indices.clone(), + ) { + false } else { - if (left_is_unit.clone() && right_opt.clone()) { + if (left_opt.clone() && right_is_unit.clone()) { true } else { - if ((left.connective.clone() == Connective::Arrow) - && (right.connective.clone() == Connective::Arrow)) - { - callable_signatures_agree(left.clone(), right.clone(), |a, b| { - node_type_compatible(a.clone(), b.clone(), source_indices.clone()) - }) + if (left_is_unit.clone() && right_opt.clone()) { + true } else { + if ((left.connective.clone() == Connective::Arrow) + && (right.connective.clone() == Connective::Arrow)) { - let left_is_container = node_is_element_collection( - left.clone(), - source_indices.clone(), - ); - let right_is_container = node_is_element_collection( - right.clone(), - source_indices.clone(), - ); - if (left_is_container.clone() && right_is_container.clone()) { - if (canonical_template_name( + callable_signatures_agree(left.clone(), right.clone(), |a, b| { + node_type_compatible( + a.clone(), + b.clone(), + source_indices.clone(), + ) + }) + } else { + { + let left_is_container = node_is_element_collection( left.clone(), source_indices.clone(), - ) != canonical_template_name( + ); + let right_is_container = node_is_element_collection( right.clone(), source_indices.clone(), - )) { - false - } else { - match left.children.clone().first().cloned() { - Some(left_ch) => { - match right.children.clone().first().cloned() { - Some(right_ch) => { - let left_el = - child_type_node(left_ch.clone()); - let right_el = - child_type_node(right_ch.clone()); - let left_el_is_unit = - is_unit_like(left_el.clone()); - let right_el_is_unit = - is_unit_like(right_el.clone()); - if (left_el_is_unit.clone() - || right_el_is_unit.clone()) - { - true - } else { - node_type_compatible( - left_el.clone(), - right_el.clone(), - source_indices.clone(), - ) + ); + if (left_is_container.clone() && right_is_container.clone()) { + if (canonical_template_name( + left.clone(), + source_indices.clone(), + ) != canonical_template_name( + right.clone(), + source_indices.clone(), + )) { + false + } else { + match left.children.clone().first().cloned() { + Some(left_ch) => { + match right.children.clone().first().cloned() { + Some(right_ch) => { + let left_el = + child_type_node(left_ch.clone()); + let right_el = + child_type_node(right_ch.clone()); + let left_el_is_unit = + is_unit_like(left_el.clone()); + let right_el_is_unit = + is_unit_like(right_el.clone()); + if (left_el_is_unit.clone() + || right_el_is_unit.clone()) + { + true + } else { + node_type_compatible( + left_el.clone(), + right_el.clone(), + source_indices.clone(), + ) + } } + std::option::Option::None => true, } - std::option::Option::None => true, } + std::option::Option::None => true, } - std::option::Option::None => true, } - } - } else { - if (((((canonical_template_name( - left.clone(), - source_indices.clone(), - ) == canonical_template_name( - right.clone(), - source_indices.clone(), - )) && (crate::v1_std_core::authored_name_at( - source_indices.clone(), - left.clone(), - ) != crate::v1_std_core::authored_name_at( - source_indices.clone(), - right.clone(), - ))) && ((left.children.clone().len() as i64) == 1)) - && ((right.children.clone().len() as i64) == 1)) - && (is_declared_container_alias_spelling( - crate::v1_std_core::authored_name_at( - source_indices.clone(), - left.clone(), - ), - ) || is_declared_container_alias_spelling( - crate::v1_std_core::authored_name_at( - source_indices.clone(), - right.clone(), - ), - ))) - { - match left.children.clone().first().cloned() { - Some(left_ch) => { - match right.children.clone().first().cloned() { - Some(right_ch) => { - let left_el = - child_type_node(left_ch.clone()); - let right_el = - child_type_node(right_ch.clone()); - if (is_unit_like(left_el.clone()) - || is_unit_like(right_el.clone())) - { - true - } else { - node_type_compatible( - left_el.clone(), - right_el.clone(), - source_indices.clone(), - ) + } else { + if (((((canonical_template_name( + left.clone(), + source_indices.clone(), + ) == canonical_template_name( + right.clone(), + source_indices.clone(), + )) && (crate::v1_std_core::authored_name_at( + source_indices.clone(), + left.clone(), + ) != crate::v1_std_core::authored_name_at( + source_indices.clone(), + right.clone(), + ))) && ((left.children.clone().len() as i64) == 1)) + && ((right.children.clone().len() as i64) == 1)) + && (is_declared_container_alias_spelling( + crate::v1_std_core::authored_name_at( + source_indices.clone(), + left.clone(), + ), + ) || is_declared_container_alias_spelling( + crate::v1_std_core::authored_name_at( + source_indices.clone(), + right.clone(), + ), + ))) + { + match left.children.clone().first().cloned() { + Some(left_ch) => { + match right.children.clone().first().cloned() { + Some(right_ch) => { + let left_el = + child_type_node(left_ch.clone()); + let right_el = + child_type_node(right_ch.clone()); + if (is_unit_like(left_el.clone()) + || is_unit_like(right_el.clone())) + { + true + } else { + node_type_compatible( + left_el.clone(), + right_el.clone(), + source_indices.clone(), + ) + } } + std::option::Option::None => true, } - std::option::Option::None => true, } + std::option::Option::None => true, } - std::option::Option::None => true, - } - } else { - if (left_opt.clone() && right_opt.clone()) { - { - let left_inner = - crate::v1_std_core::with_required_cardinality( - left.clone(), - ); - let right_inner = - crate::v1_std_core::with_required_cardinality( - right.clone(), - ); - let left_inner_is_unit = - is_unit_like(left_inner.clone()); - let right_inner_is_unit = - is_unit_like(right_inner.clone()); - if (left_inner_is_unit.clone() - || right_inner_is_unit.clone()) + } else { + if (left_opt.clone() && right_opt.clone()) { { - true + let left_inner = crate::v1_std_core::with_required_cardinality(left.clone()); + let right_inner = crate::v1_std_core::with_required_cardinality(right.clone()); + let left_inner_is_unit = + is_unit_like(left_inner.clone()); + let right_inner_is_unit = + is_unit_like(right_inner.clone()); + if (left_inner_is_unit.clone() + || right_inner_is_unit.clone()) + { + true + } else { + node_type_compatible( + left_inner.clone(), + right_inner.clone(), + source_indices.clone(), + ) + } + } + } else { + if (left_opt.clone() || right_opt.clone()) { + false } else { - node_type_compatible( - left_inner.clone(), - right_inner.clone(), - source_indices.clone(), + crate::v1_std_core::type_name_compatible( + crate::v1_std_core::authored_name_at( + source_indices.clone(), + left.clone(), + ), + crate::v1_std_core::authored_name_at( + source_indices.clone(), + right.clone(), + ), ) } } - } else { - if (left_opt.clone() || right_opt.clone()) { - false - } else { - crate::v1_std_core::type_name_compatible( - crate::v1_std_core::authored_name_at( - source_indices.clone(), - left.clone(), - ), - crate::v1_std_core::authored_name_at( - source_indices.clone(), - right.clone(), - ), - ) - } } } } @@ -2813,154 +2820,153 @@ pub fn node_type_equals_core( right: Rc, source_indices: Rc>>, ) -> bool { - if ((left.connective.clone() == Connective::Arrow) - || (right.connective.clone() == Connective::Arrow)) - { - ((left.connective.clone() == right.connective.clone()) - && callable_signatures_agree(left.clone(), right.clone(), |a, b| { - node_type_equals(a.clone(), b.clone(), source_indices.clone()) - })) + if crate::v1_compiler_coercion::text_representations_cross( + left.clone(), + right.clone(), + source_indices.clone(), + ) { + false } else { + if ((left.connective.clone() == Connective::Arrow) + || (right.connective.clone() == Connective::Arrow)) { - let left_leaf = (((left.connective.clone() == Connective::NoConnective) - && ((left.children.clone().len() as i64) == 0)) - && ((left.properties.clone().len() as i64) == 0)); - let right_leaf = (((right.connective.clone() == Connective::NoConnective) - && ((right.children.clone().len() as i64) == 0)) - && ((right.properties.clone().len() as i64) == 0)); - let left_struct = (left.connective.clone() != Connective::NoConnective); - let right_struct = (right.connective.clone() != Connective::NoConnective); - let left_name = - crate::v1_std_core::authored_name_at(source_indices.clone(), left.clone()); - let right_name = - crate::v1_std_core::authored_name_at(source_indices.clone(), right.clone()); - if (left_leaf.clone() && right_leaf.clone()) { - crate::v1_std_core::type_name_compatible(left_name.clone(), right_name.clone()) - } else { - if (left_struct.clone() && right_struct.clone()) { - if !crate::v1_std_core::type_name_compatible( - left_name.clone(), - right_name.clone(), - ) { - false - } else { - if ((left.connective.clone() == Connective::Conj) - != (right.connective.clone() == Connective::Conj)) - { + ((left.connective.clone() == right.connective.clone()) + && callable_signatures_agree(left.clone(), right.clone(), |a, b| { + node_type_equals(a.clone(), b.clone(), source_indices.clone()) + })) + } else { + { + let left_leaf = (((left.connective.clone() == Connective::NoConnective) + && ((left.children.clone().len() as i64) == 0)) + && ((left.properties.clone().len() as i64) == 0)); + let right_leaf = (((right.connective.clone() == Connective::NoConnective) + && ((right.children.clone().len() as i64) == 0)) + && ((right.properties.clone().len() as i64) == 0)); + let left_struct = (left.connective.clone() != Connective::NoConnective); + let right_struct = (right.connective.clone() != Connective::NoConnective); + let left_name = + crate::v1_std_core::authored_name_at(source_indices.clone(), left.clone()); + let right_name = + crate::v1_std_core::authored_name_at(source_indices.clone(), right.clone()); + if (left_leaf.clone() && right_leaf.clone()) { + crate::v1_std_core::type_name_compatible(left_name.clone(), right_name.clone()) + } else { + if (left_struct.clone() && right_struct.clone()) { + if !crate::v1_std_core::type_name_compatible( + left_name.clone(), + right_name.clone(), + ) { false } else { - if ((left.children.clone().len() as i64) - != (right.children.clone().len() as i64)) + if ((left.connective.clone() == Connective::Conj) + != (right.connective.clone() == Connective::Conj)) { false } else { + if ((left.children.clone().len() as i64) + != (right.children.clone().len() as i64)) { - let mut __all = true; - for pair in Rc::new( - left.children - .clone() - .iter() - .cloned() - .enumerate() - .map(|(i, v)| (i as i64, v)) - .collect::>(), - ) - .iter() - .cloned() + false + } else { { - if !(match right - .children - .clone() - .iter() - .cloned() - .skip(pair.0.clone() as usize) - .next() + let mut __all = true; + for pair in Rc::new( + left.children + .clone() + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (i as i64, v)) + .collect::>(), + ) + .iter() + .cloned() { - Some(right_child) => node_type_equals( - pair.1.clone(), - right_child.clone(), - source_indices.clone(), - ), - std::option::Option::None => false, - }) { - __all = false; - break; + if !(match right + .children + .clone() + .iter() + .cloned() + .skip(pair.0.clone() as usize) + .next() + { + Some(right_child) => node_type_equals( + pair.1.clone(), + right_child.clone(), + source_indices.clone(), + ), + std::option::Option::None => false, + }) { + __all = false; + break; + } } + __all } - __all } } } - } - } else { - if (left_leaf.clone() && right_struct.clone()) { - crate::v1_std_core::type_name_compatible( - left_name.clone(), - right_name.clone(), - ) } else { - if (left_struct.clone() && right_leaf.clone()) { + if (left_leaf.clone() && right_struct.clone()) { crate::v1_std_core::type_name_compatible( left_name.clone(), right_name.clone(), ) } else { - { - let left_is_container = node_is_element_collection( - left.clone(), - source_indices.clone(), - ); - let right_is_container = node_is_element_collection( - right.clone(), - source_indices.clone(), - ); - if (left_is_container.clone() && right_is_container.clone()) { - if (left_name.clone() != right_name.clone()) { - false - } else { - match left.children.clone().first().cloned() { - Some(left_ch) => { - match right.children.clone().first().cloned() { - Some(right_ch) => node_type_equals( - child_type_node(left_ch.clone()), - child_type_node(right_ch.clone()), - source_indices.clone(), - ), - std::option::Option::None => false, + if (left_struct.clone() && right_leaf.clone()) { + crate::v1_std_core::type_name_compatible( + left_name.clone(), + right_name.clone(), + ) + } else { + { + let left_is_container = node_is_element_collection( + left.clone(), + source_indices.clone(), + ); + let right_is_container = node_is_element_collection( + right.clone(), + source_indices.clone(), + ); + if (left_is_container.clone() && right_is_container.clone()) { + if (left_name.clone() != right_name.clone()) { + false + } else { + match left.children.clone().first().cloned() { + Some(left_ch) => { + match right.children.clone().first().cloned() { + Some(right_ch) => node_type_equals( + child_type_node(left_ch.clone()), + child_type_node(right_ch.clone()), + source_indices.clone(), + ), + std::option::Option::None => false, + } } + std::option::Option::None => false, } - std::option::Option::None => false, } - } - } else { - { - let both_maps = (node_is_keyed_collection( - left.clone(), - source_indices.clone(), - ) && node_is_keyed_collection( - right.clone(), - source_indices.clone(), - )); - if both_maps.clone() { - if (((left.children.clone().len() as i64) == 2) - && ((right.children.clone().len() as i64) == 2)) - { - match left.children.clone().first().cloned() { - Some(left_first) => match right - .children - .clone() - .first() - .cloned() - { - Some(right_first) => match left + } else { + { + let both_maps = (node_is_keyed_collection( + left.clone(), + source_indices.clone(), + ) && node_is_keyed_collection( + right.clone(), + source_indices.clone(), + )); + if both_maps.clone() { + if (((left.children.clone().len() as i64) == 2) + && ((right.children.clone().len() as i64) == 2)) + { + match left.children.clone().first().cloned() { + Some(left_first) => match right .children .clone() - .iter() + .first() .cloned() - .skip(1 as usize) - .next() { - Some(left_second) => match right + Some(right_first) => match left .children .clone() .iter() @@ -2968,53 +2974,65 @@ pub fn node_type_equals_core( .skip(1 as usize) .next() { - Some(right_second) => { - (node_type_equals( - child_type_node( - left_first.clone(), - ), - child_type_node( - right_first.clone(), - ), - source_indices.clone(), - ) && node_type_equals( - child_type_node( - left_second.clone(), - ), - child_type_node( - right_second.clone(), - ), - source_indices.clone(), - )) - } + Some(left_second) => match right + .children + .clone() + .iter() + .cloned() + .skip(1 as usize) + .next() + { + Some(right_second) => { + (node_type_equals( + child_type_node( + left_first.clone(), + ), + child_type_node( + right_first.clone(), + ), + source_indices.clone(), + ) && node_type_equals( + child_type_node( + left_second.clone(), + ), + child_type_node( + right_second + .clone(), + ), + source_indices.clone(), + )) + } + std::option::Option::None => { + false + } + }, std::option::Option::None => false, }, std::option::Option::None => false, }, std::option::Option::None => false, - }, - std::option::Option::None => false, + } + } else { + false } } else { - false - } - } else { - if (((left.params.clone().len() as i64) > 0) - && ((right.params.clone().len() as i64) > 0)) - { - callable_signatures_agree( - left.clone(), - right.clone(), - |a, b| { - node_type_equals( - a.clone(), - b.clone(), - source_indices.clone(), - ) - }, - ) - } else { - false + if (((left.params.clone().len() as i64) > 0) + && ((right.params.clone().len() as i64) > 0)) + { + callable_signatures_agree( + left.clone(), + right.clone(), + |a, b| { + node_type_equals( + a.clone(), + b.clone(), + source_indices.clone(), + ) + }, + ) + } else { + false + } } } } diff --git a/src/v2/compiler/discovery_enumeration.dag b/src/v2/compiler/discovery_enumeration.dag index b175483f1ba..5358fa5632f 100644 --- a/src/v2/compiler/discovery_enumeration.dag +++ b/src/v2/compiler/discovery_enumeration.dag @@ -1,7 +1,6 @@ module v2.compiler.discovery_enumeration import v2.std.node { Symbol } -import std.string_type { String } type ResolvedDeclRef { module: String diff --git a/src/v2/lens/effect_reach.dag b/src/v2/lens/effect_reach.dag index 6b2805d285e..5f845509d7c 100644 --- a/src/v2/lens/effect_reach.dag +++ b/src/v2/lens/effect_reach.dag @@ -83,7 +83,7 @@ fn callee_text_is_host_sink(text: String) -> Bool { } fn data_init_belongs_to_modules(decl: DataInitDecl, modules: List) -> Bool { - any(xs: modules, predicate: fn(m) { string_eq(a: decl.module, b: m) }) + any(xs: modules, predicate: fn(m) { decl.module == m }) } fn data_inits_in_modules( @@ -109,15 +109,15 @@ fn data_init_fp_matches_path(decl: DataInitDecl, path: String) -> Bool { } fn sink_kind_for_callee(callee_text: String) -> HostEffectSinkKind { - if string_eq(a: callee_text, b: "Read") - || string_eq(a: callee_text, b: "Filesystem.Read") { + if callee_text == "Read" + || callee_text == "Filesystem.Read" { FilesystemReadSink - } else if string_eq(a: callee_text, b: "WitnessBin.Run") - || string_eq(a: callee_text, b: "gunbc.WitnessBin.Run") { + } else if callee_text == "WitnessBin.Run" + || callee_text == "gunbc.WitnessBin.Run" { WitnessBinRunSink - } else if string_eq(a: callee_text, b: "Run") - || string_eq(a: callee_text, b: "shell.Exec.Run") - || string_eq(a: callee_text, b: "Exec.Run") { + } else if callee_text == "Run" + || callee_text == "shell.Exec.Run" + || callee_text == "Exec.Run" { ShellExecRunSink } else { UnknownHostEffectSink @@ -129,8 +129,8 @@ fn flow_eq(a: EffectReachFlow, b: EffectReachFlow) -> Bool { EffectReachFlow { sink: sa, path_literal: pa, init_literal_fp: fpa } => match b { EffectReachFlow { sink: sb, path_literal: pb, init_literal_fp: fpb } => - string_eq(a: pa, b: pb) - && string_eq(a: fpa, b: fpb) + pa == pb + && fpa == fpb && sink_kind_eq(a: sa, b: sb) } } @@ -184,7 +184,7 @@ fn path_literal_from_lexeme( } else { "" } - } else if string_eq(a: decl.name, b: text) && data_init_is_path_literal(decl: decl) { + } else if decl.name == text && data_init_is_path_literal(decl: decl) { decl.name } else { acc diff --git a/src/v2/lens/enforcement/grammar_coverage.dag b/src/v2/lens/enforcement/grammar_coverage.dag index df317ca6d83..7d4ea67f0ca 100644 --- a/src/v2/lens/enforcement/grammar_coverage.dag +++ b/src/v2/lens/enforcement/grammar_coverage.dag @@ -342,7 +342,7 @@ fn grammar_coverage_roster_contains_path( xs: roster, empty: false, cons: fn(acc, p) { - acc || string_eq(a: p, b: path) + acc || p == path } ) } diff --git a/src/v2/lens/live_read_classification.dag b/src/v2/lens/live_read_classification.dag index e0800a59c38..7b3675775fd 100644 --- a/src/v2/lens/live_read_classification.dag +++ b/src/v2/lens/live_read_classification.dag @@ -134,12 +134,12 @@ fn path_pattern_eq(a: PathPattern, b: PathPattern) -> Bool { match a { LiteralPath { path: pa } => match b { - LiteralPath { path: pb } => string_eq(a: pa, b: pb) + LiteralPath { path: pb } => pa == pb _ => false } ParamRef { name: na } => match b { - ParamRef { name: nb } => string_eq(a: na, b: nb) + ParamRef { name: nb } => na == nb _ => false } UnknownPath => @@ -164,7 +164,7 @@ fn live_read_carrier_eq(a: LiveReadCarrier, b: LiveReadCarrier) -> Bool { } DeclFactsReflection { home: ha } => match b { - DeclFactsReflection { home: hb } => string_eq(a: ha, b: hb) + DeclFactsReflection { home: hb } => ha == hb _ => false } } @@ -189,7 +189,7 @@ fn carrier_for_callee_text( args: List, param_names: List ) -> LiveReadCarrier { - if string_eq(a: callee_text, b: "filesystem_read") { + if callee_text == "filesystem_read" { match list_head(xs: args) { HeadFound { value: arg } => FilesystemReadPath { @@ -200,10 +200,10 @@ fn carrier_for_callee_text( } HeadAbsent => FilesystemReadPath { path_pattern: UnknownPath } } - } else if string_eq(a: callee_text, b: "module_declaration_facts") - || string_eq(a: callee_text, b: "module_declaration_facts_live") { + } else if callee_text == "module_declaration_facts" + || callee_text == "module_declaration_facts_live" { ModuleDeclarationFactsScan - } else if string_eq(a: callee_text, b: "decl_facts") { + } else if callee_text == "decl_facts" { DeclFactsReflection { home: "v2.std.decl_index" } } else { FilesystemReadPath { path_pattern: UnknownPath } @@ -722,7 +722,7 @@ fn classification_has_literal_path( RuntimeRead { carriers: carriers } => any(xs: carriers, predicate: fn(carrier) { match carrier { - FilesystemReadPath { path_pattern: LiteralPath { path: p } } => string_eq(a: p, b: path) + FilesystemReadPath { path_pattern: LiteralPath { path: p } } => p == path FilesystemReadPath { path_pattern: _ } => false ModuleDeclarationFactsScan => false DeclFactsReflection { home: _ } => false @@ -846,13 +846,13 @@ type G2RootBinding fn module_facts_for_path(path: String, nodes: List) -> List { fold_list(xs: nodes, empty: empty_module_declaration_fact_list, cons: fn(acc, n) { - if string_eq(a: n.path, b: path) { list_snoc_item(xs: acc, item: n) } else { acc } + if n.path == path { list_snoc_item(xs: acc, item: n) } else { acc } }) } fn decls_with_qualified_name(qualified: String, decls: List) -> List { fold_list(xs: decls, empty: empty_decl_list, cons: fn(acc, d) { - if string_eq(a: d.qualified_name, b: qualified) { list_snoc_item(xs: acc, item: d) } else { acc } + if d.qualified_name == qualified { list_snoc_item(xs: acc, item: d) } else { acc } }) } diff --git a/src/v2/lens/module_graph.dag b/src/v2/lens/module_graph.dag index 56ac284a42c..48a859cda5d 100644 --- a/src/v2/lens/module_graph.dag +++ b/src/v2/lens/module_graph.dag @@ -341,7 +341,7 @@ fn snoc_reference_edge_unique( edge: ModuleDependencyEdge ) -> List { if any(xs: acc, predicate: fn(existing) { - string_eq(a: existing.path, b: edge.path) && string_eq(a: existing.target_module, b: edge.target_module) + existing.path == edge.path && existing.target_module == edge.target_module }) { acc } else { @@ -851,7 +851,7 @@ fn strip_leading_dot_slash(s: String) -> String { fn path_matches_touched(closure_path: String, touched_path: String) -> Bool { let file = strip_leading_dot_slash(closure_path) let target = strip_leading_dot_slash(touched_path) - string_eq(a: file, b: target) + file == target || ends_with(s: file, suffix: target) || ends_with(s: target, suffix: file) } diff --git a/src/v2/lens/reference_deps.dag b/src/v2/lens/reference_deps.dag index 26568a9091e..1deda793764 100644 --- a/src/v2/lens/reference_deps.dag +++ b/src/v2/lens/reference_deps.dag @@ -109,8 +109,8 @@ fn reference_first_refusal_diagnostic(refusals: List) -> } fn reference_fact_eq(a: ReferenceResolutionFact, b: ReferenceResolutionFact) -> Bool { - string_eq(a: a.path, b: b.path) - && string_eq(a: a.reference_module, b: b.reference_module) + a.path == b.path + && a.reference_module == b.reference_module } fn reference_snoc_fact_unique( @@ -790,7 +790,7 @@ fn reference_import_facts_at_path( xs: import_edges, empty: acc, cons: fn(a, edge) { - if string_eq(a: edge.path, b: rel_path) { + if edge.path == rel_path { reference_snoc_fact_unique(acc: a, fact: import_edge_to_reference_fact(edge: edge)) } else { a diff --git a/src/v2/workflow/floor_grandfathered_roster.dag b/src/v2/workflow/floor_grandfathered_roster.dag index d1bbd47ac6b..bc2c4e83cb9 100644 --- a/src/v2/workflow/floor_grandfathered_roster.dag +++ b/src/v2/workflow/floor_grandfathered_roster.dag @@ -426,7 +426,7 @@ fn floor_grandfathered_chunk_030() -> List { } fn floor_grandfathered_chunk_031() -> List { - Cons { head: "test.claim.self_host_structural_text_witness_test.w_nonliteral_kernel_string_at_text_boundary_is_presently_accepted_declared_drop", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_annotated_let_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_argument_boundary_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_data_initializer_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_record_field_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_return_boundary_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_capture_acceptance_roster_is_populated", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_census_has_no_blocking_use", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_corpus_bindings_are_unambiguous", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_every_bare_row_carries_a_verdict", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_migrated_bindings_have_exact_rows", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_migrated_names_are_absent_from_the_bare_table", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_missing_source_identity_refuses", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_next_rung_representations_have_no_realization_yet", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_nominality_standing_changes_independently_of_value_semantics", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_no_realization_spells_the_bare_string_token", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_sibling_declarations_of_one_spelling_receive_no_binding", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_binding_realizes_on_rust", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_decision_is_coherent", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_declaration_resolves_to_std_string", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_reference_preserves_the_provider_declaration", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_ruling_names_a_realizable_representation", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_spelling_is_qualified_in_both_positions", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_verdict_is_migrated_not_debt", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_two_rows_for_one_declaration_refuse_as_ambiguous", tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } + Cons { head: "test.claim.self_host_structural_text_witness_test.w_nonliteral_kernel_string_at_text_return_boundary_refuses", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_annotated_let_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_argument_boundary_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_data_initializer_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_record_field_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_structural_text_witness_test.w_string_literal_at_text_return_boundary_emits_scalar_sequence", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_capture_acceptance_roster_is_populated", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_census_has_no_blocking_use", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_corpus_bindings_are_unambiguous", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_every_bare_row_carries_a_verdict", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_migrated_bindings_have_exact_rows", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_migrated_names_are_absent_from_the_bare_table", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_missing_source_identity_refuses", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_next_rung_representations_have_no_realization_yet", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_nominality_standing_changes_independently_of_value_semantics", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_no_realization_spells_the_bare_string_token", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_sibling_declarations_of_one_spelling_receive_no_binding", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_binding_realizes_on_rust", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_decision_is_coherent", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_declaration_resolves_to_std_string", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_reference_preserves_the_provider_declaration", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_ruling_names_a_realizable_representation", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_spelling_is_qualified_in_both_positions", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_symbol_verdict_is_migrated_not_debt", tail: Cons { head: "test.claim.self_host_symbol_identity_binding_witness.w_two_rows_for_one_declaration_refuse_as_ambiguous", tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } } fn floor_grandfathered_chunk_032() -> List { From 6149b5a6290df6c6aadfa63c29b83843c7e3249e Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 05:44:01 +0000 Subject: [PATCH 02/24] Import the HTTP method names the touched extdeps modules use bare The unimported-bare-provider gate judges every file a change touches, and deleting their std.string_type import touched these three. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/extdeps/docker/container_inspect.dag | 1 + dag/extdeps/docker/container_stats.dag | 1 + dag/extdeps/github/issues.dag | 1 + 3 files changed, 3 insertions(+) diff --git a/dag/extdeps/docker/container_inspect.dag b/dag/extdeps/docker/container_inspect.dag index 4ec618d5d49..13c3ef1e8ec 100644 --- a/dag/extdeps/docker/container_inspect.dag +++ b/dag/extdeps/docker/container_inspect.dag @@ -1,5 +1,6 @@ module extdeps.docker.container_inspect +import extdeps.ietf.http_semantics { GET } import std.types { Timestamp, Int, FilePath, NonEmptyStr } import std.nat { Nat } import std.measure { ByteSize, byte_size, byte_size_count, Microsecond, microsecond, microsecond_count } diff --git a/dag/extdeps/docker/container_stats.dag b/dag/extdeps/docker/container_stats.dag index 51b598bf7d7..9cdb56c8216 100644 --- a/dag/extdeps/docker/container_stats.dag +++ b/dag/extdeps/docker/container_stats.dag @@ -1,5 +1,6 @@ module extdeps.docker.container_stats +import extdeps.ietf.http_semantics { GET } import std.types { Timestamp, Int, Float } import std.nat { Nat } import std.measure { ByteSize, byte_size, byte_size_count, Nanosecond, nanosecond, nanosecond_count, Microsecond } diff --git a/dag/extdeps/github/issues.dag b/dag/extdeps/github/issues.dag index d7624a23427..68529a2a11d 100644 --- a/dag/extdeps/github/issues.dag +++ b/dag/extdeps/github/issues.dag @@ -1,5 +1,6 @@ module extdeps.github.issues +import extdeps.ietf.http_semantics { GET, PATCH, POST } import extdeps.github.github { GitHubUser, default_api_base, default_per_page } import extdeps.github.errors { GitHubErrorShape } import std.types { NonEmptyStr, Timestamp, Secret } From 4111529601edef530f64440e56a556b7db805c64 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 08:18:10 +0000 Subject: [PATCH 03/24] Keep std.string_type's String as a frontier; order host text with `<`; fix two emit-classifier defects Per neat-boar-16's ruling: `type String = FreeMonoid` in std.string_type stays, annotated as a declared frontier with no importer, pending the seed kernel-names ruling; the structural roster entry, the symbol-identity sibling row and the defork census row are restored. Its two lexicographic functions are deleted and their eight callers use host String `<` (rfc3339's three-way match becomes `<`/`==` arms). A new interpreter control asserts that host String order is code-point order on pairs a UTF-16 order would flip. Two defects in the text classifier, found as 80 E0308 in the emitted self-host compiler (emit-build): - a qualified `v2.std.text.String` spelling took its last segment and fell into the bare-String arm, so fields rendered host `String`; a qualified spelling is now classified through its module; - a bare String parameter in a module importing v2.std.text { String } was read through the by-name peel, which finds the imported alias; a declared host type now wins over the peeled views. Each has an emission regression control. The emitted self-host crate is now byte-identical to base's and builds. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/extdeps/git/enumeration.dag | 6 +-- dag/extdeps/git/git.dag | 2 +- dag/extdeps/time/rfc3339.dag | 10 ++-- dag/gunbc/fabric/fabric_cell_converge.dag | 18 ++----- dag/gunbc/fabric/fabric_cell_observe.dag | 14 ++--- dag/gunbc/guarantee_probe_corpus.dag | 3 +- ...epted_source_emits_uncompilable_target.dag | 2 +- .../rung_drop/text_boundary_identity_wall.dag | 2 +- dag/std/string_type.dag | 52 +++++-------------- ...ing_order_present_binding_witness_test.dag | 33 ++++++++++++ .../git_ref_set_decoder_witness_test.dag | 2 +- ...ry_enumeration_behavioral_witness_test.dag | 6 +++ ...t_symbol_identity_binding_witness_test.dag | 6 +-- ...xt_boundary_identity_wall_witness_test.dag | 27 +++++++++- src/v1/04_infer.dag | 13 ++++- src/v1/coercion.dag | 12 +++-- src/v1/stage0/src/v1_compiler_coercion.rs | 43 +++++++++------ src/v1/stage0/src/v1_compiler_infer.rs | 13 +++++ 18 files changed, 160 insertions(+), 104 deletions(-) diff --git a/dag/extdeps/git/enumeration.dag b/dag/extdeps/git/enumeration.dag index 8b4d519724a..13c57ea3673 100644 --- a/dag/extdeps/git/enumeration.dag +++ b/dag/extdeps/git/enumeration.dag @@ -1,7 +1,6 @@ module extdeps.git.enumeration import std.types { String, List, Bool, Int } -import std.string_type { string_is_lexicographically_before } import extdeps.git.object_store { GitObjectId, git_object_id_from_untagged_hex } import extdeps.external_authority { ExternalAuthority } import extdeps.git { git_for_each_ref_external_authority_anchor } @@ -79,7 +78,8 @@ fn git_ref_decode_admit(state: GitRefDecodeState, refs: List, id // // THE COMPARATOR IS NOT THE SAME ONE GIT USED, and that is worth stating where the check lives rather // than only where the flag is pinned. git orders refnames BYTEWISE (they are byte strings); -// string_is_lexicographically_before compares CODE POINTS. The two coincide over well-formed UTF-8 +// host String `<` compares CODE POINTS (test.claim.emitter_string_order_present_binding_witness_test +// host_string_order_is_code_point_order_in_the_interpreter). The two coincide over well-formed UTF-8 // because that encoding is order-preserving, so this check does not produce false refusals -- but the // agreement is a property of UTF-8, not of either comparator, and swapping this call for a // locale-aware one would break it while looking like an improvement. The upstream half of the fact, @@ -96,7 +96,7 @@ fn git_ref_decode_ordered_admit( match state.previous_refname { Absent => git_ref_decode_admit(state: state, refs: refs, identity: identity) Present { value: previous } => - if string_is_lexicographically_before(left: previous, right: identity.refname) { + if previous < identity.refname { git_ref_decode_admit(state: state, refs: refs, identity: identity) } else { git_ref_set_undecodable( diff --git a/dag/extdeps/git/git.dag b/dag/extdeps/git/git.dag index 509f5e7fe1d..37b439f4359 100644 --- a/dag/extdeps/git/git.dag +++ b/dag/extdeps/git/git.dag @@ -319,7 +319,7 @@ data git_ls_remote_external_authority_anchor: ExternalAuthority = ExternalAuthor // encoding -- so git's ordering here is bytewise, not a collation. // // THE CONSUMER'S COMPARATOR IS NOT BYTEWISE, AND THE TWO AGREE FOR A REASON THAT IS NOT OBVIOUS. -// extdeps.git.enumeration validates this order with std.string_type string_is_lexicographically_before, +// extdeps.git.enumeration validates this order with host String `<`, // which compares CODE POINTS. Bytewise and code-point-wise orderings coincide over well-formed UTF-8, // because UTF-8 is order-preserving by construction: a code point's encoding sorts, byte for byte, in // the same relation as the code point itself. That is a property of the encoding rather than of either diff --git a/dag/extdeps/time/rfc3339.dag b/dag/extdeps/time/rfc3339.dag index 596c4809986..72f1bd65eb5 100644 --- a/dag/extdeps/time/rfc3339.dag +++ b/dag/extdeps/time/rfc3339.dag @@ -1,8 +1,6 @@ module extdeps.time.rfc3339 import std.types { Bool, String, NonEmptyStr } -import std.algebra { Less, Equal, Greater } -import std.string_type { string_lex_compare } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } @@ -83,10 +81,8 @@ fn rfc3339_compare(left: Rfc3339Timestamp, right: Rfc3339Timestamp) -> Rfc3339Co "' do not both carry the Z designator at the same total length, so ordering them lexically would be reading the format's coincidence rather than its stated property. A caller that must order these needs a real parse."], "") } } else { - match string_lex_compare(left: left, right: right) { - Less => Rfc3339Precedes - Greater => Rfc3339Follows - Equal => Rfc3339Simultaneous - } + if left < right { Rfc3339Precedes } + else if left == right { Rfc3339Simultaneous } + else { Rfc3339Follows } } } diff --git a/dag/gunbc/fabric/fabric_cell_converge.dag b/dag/gunbc/fabric/fabric_cell_converge.dag index 3cd3845b8f1..f4328d21b64 100644 --- a/dag/gunbc/fabric/fabric_cell_converge.dag +++ b/dag/gunbc/fabric/fabric_cell_converge.dag @@ -3,7 +3,6 @@ module gunbc.fabric_cell_converge import std.types { String, List, Bool, Int, NonEmptyStr, list_length } import std.nat { Nat } import std.disposition { ConstructionMechanism, SingleAuthority } -import std.string_type { string_is_lexicographically_before } import v2.std.optional { Optional, Present, Absent } import std.measure { byte_size_count } import extdeps.systemd { @@ -938,7 +937,7 @@ fn fabric_cell_readback_names_enclosing_host(readback: FabricCellHostReadback, r // every member is by construction a DIFFERENT host from the enclosing one, and two of them may // share an index -- srv1-08 and srv4-07 do not, but srv1-07 and srv4-07 would. Host is compared // first and index breaks the tie, on the same lexicographic authority the observer's discovery uses -// (`std.string_type` string_is_lexicographically_before) rather than a second spelling of "which +// (host String `<`) rather than a second spelling of "which // name sorts first". fn fabric_cell_host_mismatch_canonical( mismatched: List, @@ -950,10 +949,7 @@ fn fabric_cell_host_mismatch_before(a: FabricCellReadbackHostMismatch, b: Fabric if (a.supplied_slot.host as String) == (b.supplied_slot.host as String) { a.supplied_slot.slot_index < b.supplied_slot.slot_index } else { - string_is_lexicographically_before( - left: a.supplied_slot.host as String, - right: b.supplied_slot.host as String, - ) + (a.supplied_slot.host as String) < (b.supplied_slot.host as String) } } @@ -1169,17 +1165,11 @@ fn fabric_cell_refusal_insert_ordered( let key = fabric_cell_observation_refusal_identity(refusal: r) concat( fold(acc, init: [], f: (kept, e) => - if string_is_lexicographically_before( - left: fabric_cell_observation_refusal_identity(refusal: e), - right: key, - ) { concat(kept, [e]) } else { kept }), + if fabric_cell_observation_refusal_identity(refusal: e) < key { concat(kept, [e]) } else { kept }), concat( [r], fold(acc, init: [], f: (kept, e) => - if string_is_lexicographically_before( - left: fabric_cell_observation_refusal_identity(refusal: e), - right: key, - ) { kept } else { concat(kept, [e]) }), + if fabric_cell_observation_refusal_identity(refusal: e) < key { kept } else { concat(kept, [e]) }), ), ) } diff --git a/dag/gunbc/fabric/fabric_cell_observe.dag b/dag/gunbc/fabric/fabric_cell_observe.dag index ee275204270..23077134140 100644 --- a/dag/gunbc/fabric/fabric_cell_observe.dag +++ b/dag/gunbc/fabric/fabric_cell_observe.dag @@ -14,7 +14,6 @@ import gunbc.fleet_runner_connectivity { fleet_committed_slot_identities, FleetHostWidthUnresolved, } -import std.string_type { string_is_lexicographically_before } import gunbc.fabric_cell_converge { FabricCellResourceAddress, FabricCellRootAddress, @@ -773,8 +772,7 @@ fn fabric_cell_discovered_entries( // observer handing the kernel an order-dependent population defeats the kernel's own canonical // order no matter how correct the kernel is. // -// THE ORDER IS LEXICOGRAPHIC OVER THE SUBJECT WIRE, on `std.string_type` -// `string_is_lexicographically_before`. That is the one total order available over a population +// THE ORDER IS LEXICOGRAPHIC OVER THE SUBJECT WIRE, by host String `<` (code-point order). That is the one total order available over a population // whose members are not all identities -- a foreign artifact has an observed NAME and no slot index // to compare -- so ordering by index, as the kernel's supplied side does, is not even expressible // here. The wire is also what the dedupe compares, so the two obligations share one projection @@ -787,17 +785,11 @@ fn fabric_cell_discovery_insert( else { concat( fold(acc, init: [], f: (kept, e) => - if string_is_lexicographically_before( - left: fabric_cell_discovered_subject_wire(s: e), - right: fabric_cell_discovered_subject_wire(s: s), - ) { concat(kept, [e]) } else { kept }), + if fabric_cell_discovered_subject_wire(s: e) < fabric_cell_discovered_subject_wire(s: s) { concat(kept, [e]) } else { kept }), concat( [s], fold(acc, init: [], f: (kept, e) => - if string_is_lexicographically_before( - left: fabric_cell_discovered_subject_wire(s: e), - right: fabric_cell_discovered_subject_wire(s: s), - ) { kept } else { concat(kept, [e]) }), + if fabric_cell_discovered_subject_wire(s: e) < fabric_cell_discovered_subject_wire(s: s) { kept } else { concat(kept, [e]) }), ), ) } diff --git a/dag/gunbc/guarantee_probe_corpus.dag b/dag/gunbc/guarantee_probe_corpus.dag index 696f7de778a..6ee15c7c296 100644 --- a/dag/gunbc/guarantee_probe_corpus.dag +++ b/dag/gunbc/guarantee_probe_corpus.dag @@ -9,7 +9,6 @@ import std.content_hash { content_hash_combine_structural, structural_content_hash, } -import std.string_type { string_is_lexicographically_before } import std.nat { Nat } import gunbc.guarantee_measurement { GuaranteeClassId, GuaranteePathId, GuaranteeProbeId, GuaranteeTargetName, @@ -898,7 +897,7 @@ fn probe_ids_are_lexicographically_ordered(ids: List) -> Bool match acc.prev { Absent => ProbeLexWalk { prev: Present { value: current }, ok: true } Present { value: prev } => - if string_is_lexicographically_before(left: prev, right: current) { + if prev < current { ProbeLexWalk { prev: Present { value: current }, ok: true } } else { ProbeLexWalk { prev: Present { value: current }, ok: false } diff --git a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag index 2f56010216d..48345935003 100644 --- a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag +++ b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag @@ -298,7 +298,7 @@ data accepted_source_emits_uncompilable_target: RecurringFailureMode = Recurring "OCCURRENCE, 2026-09-24 (wise-hawk-615, routed from stern-raven-24's native App Attest row gunbc#12251, whose emitted crate refused at rustc with 45 errors in three emitter classes and one runtime-library gap). Each was worked to its earliest unjustified boundary (DESIGN section 6b) rather than at the call site. (C) STRING ORDERING: `a < b` over Timestamp, an alias of String (extdeps.standards.rfc_5280 x509_validity_at, gunbc.auth.approval_capability utc_instant_before). Timestamp comparison needed no new typed ordering: the interpreter already orders Strings byte-lexicographically, so the defect was the emitter's. The boundary was the operand classifier v1.compiler.emit_rust rust_operand_realization_of_type, which filed the corpus String as OperandIdentityUnavailable although the type renderer realizes it as the host text carrier by is_host_text_carrier_type; ordering then refused by operator class while `==` on the same operand passed. Repair: std.operator_realization HostRealizationReason gains HostTextCarrier and the classifier reads the renderer's own predicate, so both answer one question once; is_string_comparison admits the four ordering operators through the same host-string seam as equality, and an ordering over an OPTIONAL String refuses at emission (the interpreter has no Null-vs-Str ordering; Option's None-first order would be fabricated). (A) PRESENT BINDING: `match opt { null => a o => b }` bound the whole Option in emitted code (E0308, E0609) while v1.compiler.infer narrows `o` to the present value. ALL THREE match renderings now read the checker's optional_scrutinee_binding_is_present over match_unguarded_absent_arm_index and emit `Some(o)` in exactly that arm, never re-deriving the narrowing: emit_typed_match_arm_strs for ordinary matches, and emit_typed_tco_match_arm for matches inside the tail-call lowering, which the native App Attest run surfaced separately (extdeps.standards.rfc_5280 read_extensions_list, the one error left after the first renderer was repaired); and emit_typed_match's String-literal re-emission (needs_string_from), where a review of this PR found the decision dropped -- there a string-literal arm over an optional scrutinee also emits `Some(ref __s)`, since a literal matches only a present value. (D) OCTETS: std.bytes bytes_octets and utf8_encode_bytes were interpreter-intercepted builtins whose .dag bodies were placeholders -- `[pure_dag_seam_unreachable()]`, a one-element list of `1 / 0`, and `s as Bytes`, a cast no Rust row realizes and which emitted a runtime panic. Both are now declared HostRealizedSeams (self-call bodies, std.primitive_projection rows, std.primitive_identity declarations with SourcePreservingOrder traversal facts, extdeps.languages.rust.emit rt_function_registry rows, v1.runtime_rust bodies); bytes_octets is typed List under the bounded_natural_arithmetic_evaluated_as_unbounded_int ruling, since UInt8 has no emitted realization that holds an octet and bytes_qualified_octets is the one boundary observing the range. Two further links surfaced by execution: a REALIZED seam's self-call was lowered by the name-keyed tail-call rewrite into a non-terminating loop (rust_host_seam_is_realized now exempts it, see host_seam_self_call_diverges_when_its_arm_is_absent), and rustc's deny-by-default unconditional_panic refused every crate reaching std.bytes because of pure_dag_seam_unreachable's constant `1 / 0`. The first cut relaxed that lint for all generated code; review 71098 refused it as widening a real check to admit one placeholder, and the placeholder was the earlier link: pure_dag_seam_unreachable is now a rostered HostRealizedSeam too, refusing in the interpreter with a typed error naming the seam and panicking by name in v1_rt, so the lint stays on. EVIDENCE: the fixture test.fixture.emitted_interpreted_parity.string_order_octets_present_binding exits 0 interpreted and requires the thirteen-field line `T F T F T 6 2962370309 97 0 989 ex none q` -- fields 1-5 string ordering, 6-7 octets, 8-9 the ordinary-match present binding, 10 the tail-call-match present binding, 11-13 the String-literal re-emission -- with a RED control that exits 1 printing the observed line; its emitted crate builds and runs to the same line. The enrolled claim module test.claim.emitter_string_order_present_binding_witness_test carries the emission half: host-string ordering over an alias, the named refusal for an ordering over a String? (which inference admits today, so the arm is reachable), and the present binding in all three match renderings. The integration consumer is `gunbc test //gunbc/instruments:native-app-attest` on gunbc#12251. RUNG unchanged at mitigatable for the class: required CI still neither emits-and-compiles a fixture closure nor executes integration targets, which is this row's existing trigger.", "OCCURRENCE, 2026-09-25 (loyal-boar-23, found by swift-bat-511 on native App Attest P2b): CALLING A LOCALLY BOUND FUNCTION VALUE. extdeps.apple.app_attest first_assertion_refusal folded a List AssertionRefusal?> and called the lambda-bound element `r()`; gunbc#12251 rewrote that one fold as a named AssertionStep sum, leaving the idiom refused everywhere else. The brief placed the defect in emit_rust's callee resolution; re-deriving the chain (DESIGN 6b) placed it three links earlier and found two more downstream. (1) INFERENCE, the earliest unjustified boundary: v1.compiler.infer child_type_node, the total read of a child position's type, returned a child's inferred slot whenever it had one, and an Arrow type node carries its RETURN type there (make_callable_type) -- so the element of List Int> was Int, a lambda parameter bound under it was not callable, and the call refused as \"function 'r' not found in scope\" in `gunbc run` as well as in emission. An Arrow child is now its own type -- landed independently by gunbc#12295 (receipted on arity_proxy_for_callability_misroutes_a_found_local) while this change was in review; this change consumes it rather than restating it. (2) INFERENCE, the list literal: a lambda's typed node carries its BODY type, and a list literal took its first element's type, so [fn() { none }] against List Int?> was typed List; where the declared element is an arrow and any element is a lambda the declared arrow is the element type, every element still judged against it. (3) EMISSION, two value positions of a lambda: a lambda literal in a list was a bare closure (E0308, every closure its own type), and is now the arrow carrier through the record-field row (rust_callable_field_value_wrap) bound at the rendered arrow by a typed let, not an `as` cast, which cuts the expectation typing Some(7) as Option (E0271); a let-bound lambda was `|x|` with no call site to type x from (E0282 at x.clone()) and is now emitted with typed parameters through emit_typed_collection_lambda, staying a bare closure so it still enters an impl Fn parameter. The call sites themselves (r(), f(v), g(v)) needed no change. EVIDENCE: test.fixture.emitted_interpreted_parity local_callable_value_calls exits 0 interpreted and its emitted crate, driven by a two-line harness over its library, prints the same line `6 8 6 7 9 0` with main returning ExitSuccess; with the expected line mutated both sides refuse (interpreter exit 1, emitted main ExitFailure carrying the observed line); on the pre-fix seed the fixture refuses at resolve. test.claim.local_callable_value_call_witness_test carries three inference controls (fold, unary fold and map lambda parameters over callable elements) and two emission controls (the list-literal carrier and the typed let), all five FAIL on the pre-fix seed and PASS here. RESIDUE, stated: an UNANNOTATED let-bound lambda (`let h = fn(x) { x * 2 }`) has no parameter type a Rust closure can be written at and still emits `|x: _|`-equivalent (E0282); a non-lambda element (a let-bound closure variable) in a List keeps its bare rendering. RUNG unchanged at mitigatable: required CI still neither emits-and-compiles a fixture closure nor runs its crate, which is this row's existing trigger.", "OCCURRENCE, 2026-09-24 (lively-koi-275, gunbc#12208 MQ-1): A FIELD BOUND UNDER A NESTED VARIANT PATTERN KEEPS ITS BOX. The `diagnostics` field of `v2.std.diagnostic` `Outcome` is a recursive Optional, so `v1.compiler.emit_rust` `needs_box_wrapping` boxes it. At a SINGLE-LEVEL arm `Accepted { value: v, diagnostics: d } =>` the emitter binds the field by `ref` and unboxes it (`let d = diagnostics.as_ref()`). Under a NESTED arm `Present { value: Accepted { value: facts, diagnostics: d } } =>` it bound the Box itself, and passing `d` to `v2.std.diagnostic` `diagnostics_merge` failed rustc with E0308 (expected `Option>`, found `Box>`) in the emitted `v2.compiler.infer`. The `.dag` was accepted with zero diagnostics, and only the non-required emit-build lane (`//gunbc/instruments:self-host`) saw it. The author-side repair split the match into two single-level arms, which the rest of the corpus already does by habit. That habit is an unstated workaround: it explains why the population is small and is not evidence that it is zero. The existing trigger of this row covers it: the emitted carrier comes from the declaration, and nested-pattern binders must go through the same unboxing join as single-level arms.", - "THE std.string_type COMPONENT IS DISCHARGED, 2026-09-28 (royal-newt-820, XL-0T ruling B): it now REFUSES AT CHECK in the shape it had, and its eight E0308 are gone because the shape no longer exists in the corpus. The link that was unjustified was neither of the two this row called justified -- it was the compatibility relation, which matched `String` against `String` by leaf spelling across two representations. The repair is one classifier, v1.compiler.coercion text_representation_of_type, read by the checker (corpus relation, call arguments, declared returns and data initializers, and builtin arguments, which had been admitted untyped) and by the Rust renderer. So a local `type String = FreeMonoid` (or a renamed `type Text = FreeMonoid`) handed to string_length/char_at/substring is a located refusal: test.claim.text_boundary_identity_wall_witness_test r_local_string_alias_into_kernel_primitive_refuses and r_renamed_text_alias_into_kernel_primitive_refuses. std.string_type itself keeps its two functions over the kernel String -- which is what their bodies always were -- and loses the structural declaration; its 71 importers were nicknaming host text as that declaration and their imports are deleted, including src/v2/compiler/discovery_enumeration.dag, whose emitted closure no longer contains a structural std.string_type. THE TWO OPEN SHAPES THE ENTRY ABOVE ROUTED FOR A RULING were decided as B (two carriers, a declared crossing): a host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node keyed to the existing literal_homomorphism_rows row, and every other crossing refuses. The residue and its trigger are carried by gunbc.rung_drop text_boundary_identity_wall, not restated here.", + "THE std.string_type COMPONENT IS DISCHARGED, 2026-09-28 (royal-newt-820, XL-0T ruling B): it now REFUSES AT CHECK in the shape it had, and its eight E0308 are gone because the shape no longer exists in the corpus. The link that was unjustified was neither of the two this row called justified -- it was the compatibility relation, which matched `String` against `String` by leaf spelling across two representations. The repair is one classifier, v1.compiler.coercion text_representation_of_type, read by the checker (corpus relation, call arguments, declared returns and data initializers, and builtin arguments, which had been admitted untyped) and by the Rust renderer. So a local `type String = FreeMonoid` (or a renamed `type Text = FreeMonoid`) handed to string_length/char_at/substring is a located refusal: test.claim.text_boundary_identity_wall_witness_test r_local_string_alias_into_kernel_primitive_refuses and r_renamed_text_alias_into_kernel_primitive_refuses. std.string_type's two functions are deleted (their eight callers use host String `<`, which is the same code-point order), and its `type String = FreeMonoid` stays as a declared frontier with no importer pending the seed kernel-names ruling; its 71 importers were nicknaming host text as that declaration and their imports are deleted, including src/v2/compiler/discovery_enumeration.dag, whose emitted closure no longer reaches std.string_type. THE TWO OPEN SHAPES THE ENTRY ABOVE ROUTED FOR A RULING were decided as B (two carriers, a declared crossing): a host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node keyed to the existing literal_homomorphism_rows row, and every other crossing refuses. The residue and its trigger are carried by gunbc.rung_drop text_boundary_identity_wall, not restated here.", ], diff --git a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag index 8a49cf6716f..dc8d27cbee2 100644 --- a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag +++ b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag @@ -12,5 +12,5 @@ data text_boundary_identity_wall: RungDrop = RungDrop { standing: Standing, - declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it), std.string_type's own `type String = FreeMonoid` is deleted because its bodies are kernel host-text primitives, and v1.compiler.coercion structural_declaration_modules_for loses the dag/std/string_type.dag entry. ONE SEED CONSEQUENCE, REPAIRED WITH IT: with the duplicate gone, v1.compiler.infer_env global_bare_lookup found ONE corpus String and bound every service-output field spelled String to v2.std.text from anywhere; a kernel spelling now never resolves through the global bare fallback. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } + declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } } diff --git a/dag/std/string_type.dag b/dag/std/string_type.dag index e05cf20f499..b7a4926a573 100644 --- a/dag/std/string_type.dag +++ b/dag/std/string_type.dag @@ -1,41 +1,15 @@ module std.string_type -import std.algebra { Ordering, Less, Equal, Greater, ordering_is_less } - -// LEXICOGRAPHIC ORDER OVER HOST TEXT. The parameters are the kernel String because the bodies -// are the kernel string primitives (string_length, char_at, substring, code_point), whose contract -// is host text. This module once also declared `type String = FreeMonoid` and typed these -// same parameters with it, so every call to a primitive was a crossing from the code-point -// sequence to host text with no unfold -- accepted by the checker and refused by rustc (eight -// E0308 in string_lex_compare). The structural text carrier is v2.std.text String; a second -// structural declaration here had no consumer that used it structurally -// (gunbc.rung_drop text_boundary_identity_wall). - -fn string_lex_compare(left: String, right: String) -> Ordering { - let left_len = string_length(s: left) - let right_len = string_length(s: right) - if left_len == 0 && right_len == 0 { - Equal - } else if left_len == 0 { - Less - } else if right_len == 0 { - Greater - } else { - let left_cp = code_point(c: char_at(s: left, pos: 0)) - let right_cp = code_point(c: char_at(s: right, pos: 0)) - if left_cp < right_cp { - Less - } else if left_cp > right_cp { - Greater - } else { - string_lex_compare( - left: substring(s: left, start: 1, end: left_len), - right: substring(s: right, start: 1, end: right_len), - ) - } - } -} - -fn string_is_lexicographically_before(left: String, right: String) -> Bool { - ordering_is_less(o: string_lex_compare(left: left, right: right)) -} +import std.algebra { FreeMonoid } +import std.types { Char } + +// A DECLARED FRONTIER, NOT A CONSUMED TYPE (DESIGN section 3c). This declaration grounds +// String = FreeMonoid -- strings over an alphabet are the free monoid -- per operator Ruling 3 +// (2026-07-15, cited at std.coercion beside grounded_primitive_coproduct_identities). It has no +// importer: the 71 modules that imported it treated the value as host text and now bind the kernel +// String, and its two lexicographic functions were deleted because host String ordering (`<`) is +// already that order (gunbc.rung_drop text_boundary_identity_wall). The open seed kernel-names +// de-fork ruling (String/Bool/Float/List) retires this declaration or promotes it; until then it +// stays, and the structural roster (v1.compiler.coercion structural_declaration_modules_for) keeps +// it a code-point sequence. +type String = FreeMonoid diff --git a/dag/test/claim/emitter_string_order_present_binding_witness_test.dag b/dag/test/claim/emitter_string_order_present_binding_witness_test.dag index 88188a9a097..fdec595a602 100644 --- a/dag/test/claim/emitter_string_order_present_binding_witness_test.dag +++ b/dag/test/claim/emitter_string_order_present_binding_witness_test.dag @@ -64,10 +64,43 @@ fn w_present_binding_in_the_string_literal_reemission() -> Bool { ) } +// HOST STRING ORDER IS CODE-POINT ORDER, asserted rather than assumed, because callers rely on it +// (extdeps.git.enumeration, gunbc.guarantee_probe_corpus, gunbc.fabric.fabric_cell_observe and +// fabric_cell_converge order with `<` on the kernel String and state CODE-POINT order). The +// interpreter compares Str by Rust str ordering and the emitted Rust by String's Ord; both are +// bytewise over UTF-8, and UTF-8 preserves scalar order. The discriminating pair is U+FFFD against +// U+10000: code-point order puts U+FFFD first, while a UTF-16 code-unit order would put the +// surrogate-encoded U+10000 (0xD800 ...) first. U+D7FF / U+E000 straddle the surrogate gap, and +// "z" / U+E9 is the ordinary Latin-1 case. +fn w_host_string_order_is_code_point_order_in_the_interpreter() -> Bool { + "z" < "\u{e9}" + && "\u{fffd}" < "\u{10000}" + && !("\u{10000}" < "\u{fffd}") + && "\u{d7ff}" < "\u{e000}" + && "\u{e9}" < "\u{fffd}" +} + +fn w_non_ascii_string_ordering_realizes_on_the_host_string() -> Bool { + compile_dag_rust_emit_check( + "module sordna\n\nfn before(a: String) -> Bool {\n a < \"\\u{10000}\"\n}\n", + "src/sordna.rs", + ["a.clone() <"], + ["compile_error!", ".chars()"] + ) +} + test fn string_ordering_realizes_on_the_host_string() -> Bool { w_string_ordering_realizes_on_the_host_string() } +test fn host_string_order_is_code_point_order_in_the_interpreter() -> Bool { + w_host_string_order_is_code_point_order_in_the_interpreter() +} + +test fn non_ascii_string_ordering_realizes_on_the_host_string() -> Bool { + w_non_ascii_string_ordering_realizes_on_the_host_string() +} + test fn optional_string_ordering_refuses_at_emission() -> Bool { w_optional_string_ordering_refuses_at_emission() } diff --git a/dag/test/claim/git_ref_set_decoder_witness_test.dag b/dag/test/claim/git_ref_set_decoder_witness_test.dag index 2104cd85826..83e9f94c97c 100644 --- a/dag/test/claim/git_ref_set_decoder_witness_test.dag +++ b/dag/test/claim/git_ref_set_decoder_witness_test.dag @@ -156,7 +156,7 @@ test fn nonzero_exit_refuses_the_read_and_preserves_its_cause() -> Bool { // THE COMPARATOR'S RELATION TO GIT'S, MECHANISED RATHER THAN ONLY ASSERTED. The ordering witnesses // above establish that the check refuses a decreasing pair; they say nothing about WHICH ORDER it is // enforcing, and that is the fact the enumeration annotation actually claims. git orders refnames -// BYTEWISE; string_is_lexicographically_before compares CODE POINTS; the two agree over well-formed +// BYTEWISE; host String `<` compares CODE POINTS; the two agree over well-formed // UTF-8 because that encoding is order-preserving. // // THE DISCRIMINATING PAIR IS ASCII-vs-MULTIBYTE, chosen because it separates our comparator from the diff --git a/dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag b/dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag index cd6b8a551d3..2f54afec5b6 100644 --- a/dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag +++ b/dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag @@ -15,6 +15,12 @@ import tools.self_host_module_behavioral_transport_roster { compiler_module_beha // gunbc.recurring_failure_mode accepted_source_emits_uncompilable_target. Every error is in // that file (the driver is not reached, since the library it links fails); this receipt greens when that component is // discharged, and not by re-pointing this module's String import around it. +// COMPONENT DISCHARGED AT SOURCE (2026-09-28, gunbc.rung_drop text_boundary_identity_wall): +// string_lex_compare and string_is_lexicographically_before are deleted (host String `<` is that +// order), and the shape that produced the eight E0308 -- a FreeMonoid value handed to a +// host-text primitive -- now refuses at check. This module's std.string_type import was a nickname +// for the kernel String and is deleted with the other 70, not re-pointed at v2.std.text. The receipt +// is a wet run; its green after the discharge has not been observed yet. test fn self_host_discovery_enumeration_behavioral_receipt_holds() -> Bool { compiler_module_behavioral_receipt_for(module_path: "src/v2/compiler/discovery_enumeration.dag") diff --git a/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag b/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag index 93d8d690174..66d9aefa43a 100644 --- a/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag +++ b/dag/test/claim/self_host_symbol_identity_binding_witness_test.dag @@ -62,14 +62,14 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // THE SAME-SPELLING SIBLINGS, all REAL declarations (the cited-declaration gate refuses a citation // of a declaration that does not exist, so the "another module declaring Symbol" falsifier is -// asked of the declarations the corpus actually has two of): v2.std.text String beside the kernel -// String row (std.string_type's second structural String was deleted with its nickname importers, -// gunbc.rung_drop text_boundary_identity_wall), v2.std.logic Bool beside std.types Bool (which HAS an exact +// asked of the declarations the corpus actually has two of): v2.std.text String and std.string_type +// String beside the kernel String row, v2.std.logic Bool beside std.types Bool (which HAS an exact // row), and v2.std.node Hash, declared in Symbol's own module one line below it. None may receive a // binding authored for a different declaration -- module-path and decl-name discrimination both. fn sibling_declarations_without_bindings() -> List { [ decl_ref(module_path: "v2.std.text", decl_name: "String"), + decl_ref(module_path: "std.string_type", decl_name: "String"), decl_ref(module_path: "v2.std.logic", decl_name: "Bool"), decl_ref(module_path: "v2.std.node", decl_name: "Hash") ] diff --git a/dag/test/claim/text_boundary_identity_wall_witness_test.dag b/dag/test/claim/text_boundary_identity_wall_witness_test.dag index c296c139788..5c776554aa1 100644 --- a/dag/test/claim/text_boundary_identity_wall_witness_test.dag +++ b/dag/test/claim/text_boundary_identity_wall_witness_test.dag @@ -41,7 +41,7 @@ fn admitted(source: String) -> Bool { // THE SHAPE std.string_type HAD: a local `type String = FreeMonoid` whose own body hands the // value to a kernel host-text primitive. The checker admitted it and rustc refused the emission -// (eight E0308 in string_lex_compare). +// (eight E0308 in the since-deleted string_lex_compare). test fn r_local_string_alias_into_kernel_primitive_refuses() -> Bool { refuses(source: "module probe_local_string_alias\nimport std.algebra { FreeMonoid }\nimport std.types { Char }\ntype String = FreeMonoid\nfn text_len(t: String) -> Int { string_length(s: t) }\n") } @@ -149,3 +149,28 @@ test fn c_kernel_literal_at_qualified_text_parameter_is_admitted() -> Bool { test fn c_markup_attribute_value_if_join_is_admitted() -> Bool { admitted(source: "module probe_script_src_attr\nimport std.markup { Attribute }\nfn script_src_attr(attributes: List) -> String {\n fold(attributes, init: \"\", f: (acc, a) => if a.name == \"src\" { a.value } else { acc })\n}\n") } + +// EMISSION CONTROLS FOR THE ONE CLASSIFIER, one per defect it had on the way (each found as E0308 in +// the emitted self-host compiler, v2_compiler_tokenize.rs). A QUALIFIED `v2.std.text.String` field is +// the code-point carrier, not host text: taking the last segment of the qualified spelling made the +// renderer emit `String` for it. +test fn e_qualified_text_field_renders_the_code_point_carrier() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_qualified_field_probe\nimport v2.std.text { string_is_empty }\ntype Carrier { t: v2.std.text.String }\nfn probe(c: Carrier) -> Int { 0 }\n", + "src/test_claim_text_qualified_field_probe.rs", + ["pub t: Rc Bool { + compile_dag_rust_emit_check( + "module test.claim.text_bare_param_probe\nimport v2.std.text { String }\nfn takes(text: String) -> Int { 0 }\nfn probe() -> Int { takes(text: \".\") }\n", + "src/test_claim_text_bare_param_probe.rs", + ["\".\".to_string()"], + ["vec![46]"] + ) +} diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index cc0f06acb65..8a6a02e89ed 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -6915,8 +6915,19 @@ fn host_text_unfolded_at_code_point_boundary(value: Node, destination_type: Node // parameter declared with the QUALIFIED v2.std.text.String is peeled to the FreeMonoid coproduct // declaration itself, which has lost its Char argument; its substitution basis still carries // FreeMonoid, and where it too is generalized the declared type still does. Reading only the -// first let a host value reach a qualified text parameter with no unfold. Absent means neither view is a code-point sequence. +// first let a host value reach a qualified text parameter with no unfold. But a DECLARED type that is +// host text wins over both peeled views: a bare `String` in a module that imports v2.std.text +// { String } is the kernel String (kernel precedence; test.claim.self_host_structural_text_witness_test +// w_bare_string_with_text_import_stays_host_deterministically), while the by-name peel in that module +// finds the imported structural alias. Absent means neither view is a code-point sequence. fn formal_code_point_view(formal: ResolvedFormal, source_indices: Map) -> Node? { + match text_representation_of_type(n: formal.declared_type, source_indices: source_indices) { + HostText => none + _ => formal_code_point_view_peeled(formal: formal, source_indices: source_indices) + } +} + +fn formal_code_point_view_peeled(formal: ResolvedFormal, source_indices: Map) -> Node? { match text_representation_of_type(n: formal.declaration_bound_conformance, source_indices: source_indices) { CodePointSequence => Present { value: formal.declaration_bound_conformance } _ => match text_representation_of_type(n: formal.substitution_basis, source_indices: source_indices) { diff --git a/src/v1/coercion.dag b/src/v1/coercion.dag index 9a053a23397..a495cee1eac 100644 --- a/src/v1/coercion.dag +++ b/src/v1/coercion.dag @@ -136,7 +136,7 @@ data type_reference_identity_note: String = "A realization decision is made at R fn structural_declaration_modules_for(dag_name: String) -> List { match dag_name { "Hash" => ["src/v2/std/node.dag"] - "String" => ["src/v2/std/text.dag"] + "String" => ["src/v2/std/text.dag", "dag/std/string_type.dag"] "Bool" => ["src/v2/std/logic.dag"] _ => [] } @@ -224,9 +224,10 @@ fn provenance_is_corpus_declared(p: TypeDeclarationProvenance) -> Bool { } } -// A LOCAL ALIAS OF A QUALIFIED TEXT REFERENCE (`type LocalText = v2.std.text.String`) resolves to a +// A QUALIFIED `...String` SPELLING, written directly or reached through a local alias (`type +// LocalText = v2.std.text.String`, which resolves to a // childless leaf that keeps its authored spelling and carries the qualified target only as its -// name. That target is a code-point sequence exactly when its module is on the structural roster. +// name) names its target module. That target is a code-point sequence exactly when its module is on the structural roster. // The roster is keyed by file path, so the module path is mapped onto that path here -- the same // positional residue structural_declaration_modules_for already declares, not a second authority. fn qualified_string_names_structural_declaration(qualified: String) -> Bool { @@ -236,7 +237,8 @@ fn qualified_string_names_structural_declaration(qualified: String) -> Bool { } fn text_representation_of_type(n: Node, source_indices: Map) -> TextRepresentation { - let authored = qualified_last_segment(name: authored_name_at(source_indices: source_indices, node: n)) + let authored_full = authored_name_at(source_indices: source_indices, node: n) + let authored = qualified_last_segment(name: authored_full) let resolved_name = qualified_last_segment(name: n.name) let element = qualified_last_segment(name: text_carrier_element_name(n: n, source_indices: source_indices)) let p = type_reference_provenance(n: n) @@ -247,6 +249,8 @@ fn text_representation_of_type(n: Node, source_indices: Map String { pub fn structural_declaration_modules_for(dag_name: String) -> Rc> { match dag_name.clone().as_str() { "Hash" => Rc::new(vec!["src/v2/std/node.dag".to_string()]), - "String" => Rc::new(vec!["src/v2/std/text.dag".to_string()]), + "String" => Rc::new(vec![ + "src/v2/std/text.dag".to_string(), + "dag/std/string_type.dag".to_string(), + ]), "Bool" => Rc::new(vec!["src/v2/std/logic.dag".to_string()]), _ => Rc::new(vec![]), } @@ -268,9 +271,8 @@ pub fn text_representation_of_type( source_indices: Rc>>, ) -> TextRepresentation { { - let authored = crate::v1_std_core::qualified_last_segment( - crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()), - ); + let authored_full = crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()); + let authored = crate::v1_std_core::qualified_last_segment(authored_full.clone()); let resolved_name = crate::v1_std_core::qualified_last_segment(n.name.clone()); let element = crate::v1_std_core::qualified_last_segment(text_carrier_element_name( n.clone(), @@ -302,24 +304,35 @@ pub fn text_representation_of_type( TextRepresentation::NotText } } else { - if (authored.clone() == "String".to_string()) { - if ((element.clone() == "Char".to_string()) - && provenance_is_corpus_declared(p.clone())) - { + if ((authored.clone() == "String".to_string()) + && v1_rt::contains(authored_full.clone(), ".".to_string())) + { + if qualified_string_names_structural_declaration(authored_full.clone()) { TextRepresentation::CodePointSequence } else { - if (element.clone() != "".to_string()) { - TextRepresentation::NotText + TextRepresentation::NotText + } + } else { + if (authored.clone() == "String".to_string()) { + if ((element.clone() == "Char".to_string()) + && provenance_is_corpus_declared(p.clone())) + { + TextRepresentation::CodePointSequence } else { - if provenance_declares_structurally("String".to_string(), p.clone()) { - TextRepresentation::CodePointSequence + if (element.clone() != "".to_string()) { + TextRepresentation::NotText } else { - TextRepresentation::HostText + if provenance_declares_structurally("String".to_string(), p.clone()) + { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::HostText + } } } + } else { + TextRepresentation::NotText } - } else { - TextRepresentation::NotText } } } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 67a422cb20e..20a62644360 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -10491,6 +10491,19 @@ pub fn host_text_unfolded_at_code_point_boundary( pub fn formal_code_point_view( formal: Rc, source_indices: Rc>>, +) -> Option> { + match crate::v1_compiler_coercion::text_representation_of_type( + formal.declared_type.clone(), + source_indices.clone(), + ) { + TextRepresentation::HostText => std::option::Option::None, + _ => formal_code_point_view_peeled(formal.clone(), source_indices.clone()), + } +} + +pub fn formal_code_point_view_peeled( + formal: Rc, + source_indices: Rc>>, ) -> Option> { match crate::v1_compiler_coercion::text_representation_of_type( formal.declaration_bound_conformance.clone(), From c26ef54f9c0aea9ca0fe160c5d6d68f785dd53c7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 08:53:43 +0000 Subject: [PATCH 04/24] Claim-scope ambiguity wall: a kernel spelling is never contested AmbiguousBareNameRead counted a bare String as contested between std.string_type and v2.std.text in the 39 modules whose std.string_type import this change deleted. A kernel or container spelling binds the substrate; the wall now reads is_substrate_vocabulary, the rule every other bare-name producer already reads. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/stage0/src/cli_run.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 9d3ae54f573..e9f9dc95e2c 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -42756,6 +42756,14 @@ fn claim_scope_for_with_memos( }; let site_file = module.module.span.file.as_str(); for name in refs.iter() { + // A kernel or container spelling binds the substrate, never a declaring module + // (`is_substrate_vocabulary`, the one rule every bare-name producer reads), so two + // corpus modules declaring `String` do not make a bare `String` contested. This + // held by accident while some import in each scope named one of the declarers; + // gunbc.rung_drop text_boundary_identity_wall deleted 71 such imports. + if is_substrate_vocabulary(name) { + continue; + } let Some(claimants) = ambiguous.get(name) else { continue; }; From 1b2a372bfda5f745845105783481627017d991e6 Mon Sep 17 00:00:00 2001 From: "gunbai-bot[bot]" <289086189+gunbai-bot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 09:50:13 +0000 Subject: [PATCH 05/24] chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-rung-drops.md Ledger-Rows-Repaired: docs/design-rung-drops.md text_boundary_identity_wall Heal-Candidate-Run: 36400142162 --- docs/design-rung-drops.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index b802182a7f7..e45bcb3c94b 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -124,6 +124,8 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. + ### Fabric CI evidence lane as a required merge block, and then as a lane at all — declared 2026-08-31 **AMENDED 2026-09-04: THE LANE NO LONGER EXECUTES EITHER, AND THIS ROW NOW COVERS BOTH LOSSES.** The 2026-09-04 operator ruling on runner capacity deleted the `fabric-evidence` JOB, together with `fabric_ci_evidence_calibration_step`, its two derived bounds, and `tools/fabric_ci_evidence_calibration.sh`. The declaration below was written when only the merge block was withdrawn and the calibration still ran; read every present-tense claim in it about the lane EXECUTING in the past tense. WHAT CHANGES: the temporary rung falls from mitigatable to outside the modeled guarantee — there is no longer a red for a human to read, because there is no run. WHAT DOES NOT: the restoration trigger, which was already stated at capability grain and is unchanged, and the operational consequence, which now binds harder — the FABRIC-CI program (warm-seal-35) agreed to check `fabric-evidence` explicitly on every head rather than inferring it from `witnesses`, and there is now nothing to check, so any conclusion resting on that lane rests on nothing. THE ROW IS AMENDED RATHER THAN SUPERSEDED because the subject is the same lane and the same trigger retires both losses; a second row would have split one subject across two authorities. The countability moved: `w_RED_fabric_evidence_executes_without_gating` asserted the lane still ran and is replaced by `test.claim.witness_floor_workflow_consolidation_witness_test` `w_RED_the_deleted_lanes_do_not_return`, which reds if the job, the FABRIC_EVIDENCE binding or the calibration argv returns while this row stands. The original declaration follows. **THE FABRIC-CI EVIDENCE LANE STOPS BLOCKING MERGES AND KEEPS EXECUTING (2026-08-31).** #9704 added `fabric-evidence` as a fourth job and, in the same change, made it a `needs` of the aggregating `witnesses` job that the `passing CI` ruleset names as this repository's one required context. PREVIOUS RUNG: mechanically preventable — a defect in the FCI-EVIDENCE-0 run-boundary contract was exposed by an enrolled calibration and blocked the merge. TEMPORARY RUNG: mitigatable — the same calibration still runs on every push and pull request, still refuses loudly, and its log is still the record; what is gone is the block, so a red is now a signal a human must read rather than a wall. REASON, and it is wall clock rather than doubt about the evidence: the required context's wall is the MAX over its three lanes, and on run 33350499023 that max was `fabric-evidence` at 27 minutes against 20 for the floor lane and 10.5 for the build lane, having moved the required wall from ~13 minutes to ~28-40 within a day of #9704 landing. 1469 of those 1620 seconds are the calibration step, and its own logs put all ~18 of the plan's `gunbc run` host processes at 80-90 seconds each. That cost is not the evidence: the instrument's import closure is six modules, and per `cli_run.entry_resolve` an `--entry` run is scoped in what it EMITS but WHOLE-TREE in what it PARSES, so each process re-parses and re-censuses the entire 4414-module corpus to evaluate a handful of rows. The lane is paying eighteen whole-corpus censuses (DESIGN §2 duplicated work) for thirteen assertions. Batching them is NOT the repair and is not what this row defers: the calibration-timeout derivation recorded that distinct host processes ARE the process-edge subject (that row was deleted with the lane on 2026-09-04; its reasoning is preserved in this sentence), so fewer processes would remove the boundary rather than observe it more cheaply. BOUNDED POPULATION: exactly one job, `fabric-evidence`, and exactly one lost edge — its `needs.fabric-evidence.result` binding into the aggregate's closed-vocabulary verdict fold. No other lane, phase, witness or claim changes rung; the build and floor lanes keep every edge they had, and the calibration keeps every assertion it had. The drop is countable from the emitted workflow: `test.claim.witness_floor_workflow_consolidation_witness_test` `w_RED_fabric_evidence_executes_without_gating` reds if the job stops executing, and reds again if either gating spelling returns while this row still stands. ONE OPERATIONAL CONSEQUENCE, recorded here because it is the kind of practice that quietly stops once the person who agreed to it moves on. With the needs edge withdrawn, a `fabric-evidence` failure no longer reaches the aggregate verdict, so any lane that was reading the required context as a proxy for that lane's health is now reading a context that cannot see it. The FABRIC-CI program (warm-seal-35) accepted the drop on that basis and will check `fabric-evidence` EXPLICITLY on every head rather than inferring it from `witnesses`. Anyone else joining on the required context owes the same explicit check for as long as this row stands; that obligation ends when the trigger below is met and not before. RESTORATION TRIGGER: the CAPABILITY of a single fabric evidence process resolving its entry against the source roots at a cost that fits the required wall budget — concretely, the whole-tree parse and name census being paid once per tree state rather than once per host process, so that the 13-row plan executes its full process-edge roster inside the lane's share of a ~10-minute required wall. THIS IS NOT RETIRED BY THE MEMO PR MERGING. It is retired by a measured `fabric-evidence` job wall that leaves the required context at or under its budget with the needs edge and the FABRIC_EVIDENCE binding restored, which is the only thing that makes the block affordable again. Reducing the plan's process count, relaxing what a row asserts, or moving the calibration to a cadence does not satisfy this trigger — the first removes the subject, and the other two are different drops that would need their own rows. From 30a45968dc69a798dcb6ebe9861cc10a00385c7b Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 10:54:45 +0000 Subject: [PATCH 06/24] Enroll a control for the kernel-spelling skip in the claim-scope ambiguity wall Two claimants declare String, the reader uses String bare in a service exit arm (the shape the floor refused in 39 extdeps modules). The head binary accepts the scope; the base binary refuses it as AmbiguousBareNameRead. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../bare_name_ambiguity_wall_witness_test.dag | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dag/test/claim/bare_name_ambiguity_wall_witness_test.dag b/dag/test/claim/bare_name_ambiguity_wall_witness_test.dag index c793df16073..5bc9bc8839d 100644 --- a/dag/test/claim/bare_name_ambiguity_wall_witness_test.dag +++ b/dag/test/claim/bare_name_ambiguity_wall_witness_test.dag @@ -236,3 +236,32 @@ test fn the_refusal_carries_a_pasteable_import_line() -> Bool { o: amb_scope_over(user_source: amb_bare_read_user_source) ) } + +// ROW 6 -- A KERNEL SPELLING IS NEVER CONTESTED. Both claimants declare `String` beside their +// pullers and the reader uses `String` bare in value position, as service declarations do. The +// kernel String binds the substrate, not either declarer (cli_run is_substrate_vocabulary, read by +// claim_scope_for_with_memos since gunbc.rung_drop text_boundary_identity_wall deleted the 71 +// std.string_type imports that used to hide this). The read is a service exit arm's `String`, the +// shape the floor refused in 39 extdeps modules. MUST NOT REFUSE; without that skip the wall +// refuses this manifest as AmbiguousBareNameRead. +data amb_kernel_x_source: String = "module amb\n\nfn only_amb() -> Int {\n 1\n}\n\ntype String = Int\n" +data amb_kernel_y_source: String = "module elsewhere.y\n\nfn only_elsewhere() -> Int {\n 2\n}\n\ntype String = Int\n" +data amb_kernel_user_source: String = "module amb.user\n\nservice probe.Svc {\n operation Run {\n input { a: Int }\n output { r: Int from \"r\" }\n transport shell { argv: [\"true\"] }\n exit {\n 0 => Unit\n nonzero => String \"failed\"\n }\n mock_response {\n 0 => { r: 1 } \"ok\"\n }\n }\n}\n\nfn pull() -> Int {\n only_amb() + only_elsewhere()\n}\n" + +fn amb_kernel_scope() -> ClaimScopeFixtureOutcome { + claim_scope_fixture( + MultiModuleCompileFixture { + sources: [ + fixture_source("amb.dag", amb_kernel_x_source), + fixture_source("elsewhere/y.dag", amb_kernel_y_source), + fixture_source("amb/user.dag", amb_kernel_user_source) + ], + entry: "amb/user.dag" + }, + "amb.user" + ) +} + +test fn a_kernel_spelling_declared_twice_is_not_contested() -> Bool { + claim_scope_was_accepted(o: amb_kernel_scope()) +} From 843ffb4467a02186b2456beef624822dde936e82 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 11:54:42 +0000 Subject: [PATCH 07/24] Rename the namecheap test helper response -> namecheap_api_response A top-level fn named response (from #12421) made the unimported-bare-provider gate read every service declaration's response block as a bare use of it in the extdeps files this PR touches. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../claim/namecheap_hosts_witness_test.dag | 18 +++++++++--------- .../namecheap_publication_witness_test.dag | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dag/test/claim/namecheap_hosts_witness_test.dag b/dag/test/claim/namecheap_hosts_witness_test.dag index e4506caf9fe..585aaf2e9e7 100644 --- a/dag/test/claim/namecheap_hosts_witness_test.dag +++ b/dag/test/claim/namecheap_hosts_witness_test.dag @@ -3,23 +3,23 @@ import std.types { String, Bool } import extdeps.namecheap.read_hosts { namecheap_read_hosts, NamecheapHostsObserved, NamecheapHostsRefused } import extdeps.languages.xml.read { read_xml_subset, XmlRefused, XmlParsed } -fn response(result: String) -> String { +fn namecheap_api_response(result: String) -> String { join(["namecheap.domains.dns.getHosts", result, ""], "") } fn result(domain: String, rows: String) -> String { join(["", rows, ""], "") } test fn valid_host_preserves_unknown_provider_fields() -> Bool { - match namecheap_read_hosts(body: response(result: result(domain: "gunb.ai", rows: "")), domain: "gunb.ai") { + match namecheap_read_hosts(body: namecheap_api_response(result: result(domain: "gunb.ai", rows: "")), domain: "gunb.ai") { NamecheapHostsObserved { domain: observed_domain, hosts, result_fields: _ } => observed_domain == "gunb.ai" && count(hosts) == 1 && all(hosts, h => h.fields.any(f => f.name == "Future" && f.value == "preserved") && h.fields.any(f => f.name == "Address" && f.value == "a&b")) _ => false } } test fn wrong_domain_refuses() -> Bool { - match namecheap_read_hosts(body: response(result: result(domain: "other.ai", rows: "")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: result(domain: "other.ai", rows: "")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn incomplete_host_refuses() -> Bool { - match namecheap_read_hosts(body: response(result: result(domain: "gunb.ai", rows: "")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: result(domain: "gunb.ai", rows: "")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn duplicate_attributes_refuse() -> Bool { match read_xml_subset(s: "") { XmlRefused => true _ => false } @@ -34,13 +34,13 @@ test fn doctype_and_external_entity_refuse() -> Bool { match read_xml_subset(s: "]>&x;") { XmlRefused => true _ => false } } test fn encoded_markup_is_text_not_a_second_host() -> Bool { - match namecheap_read_hosts(body: response(result: result(domain: "gunb.ai", rows: "<Host Name='fake'/>")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: result(domain: "gunb.ai", rows: "<Host Name='fake'/>")), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn nested_namespace_override_refuses() -> Bool { - match namecheap_read_hosts(body: response(result: ""), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: ""), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn duplicate_results_refuse() -> Bool { - match namecheap_read_hosts(body: response(result: concat(result(domain: "gunb.ai", rows: ""), result(domain: "gunb.ai", rows: ""))), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: concat(result(domain: "gunb.ai", rows: ""), result(domain: "gunb.ai", rows: ""))), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn malformed_declaration_refuses() -> Bool { @@ -54,12 +54,12 @@ test fn literal_attribute_newline_refuses_instead_of_misnormalizing() -> Bool { } test fn duplicate_provider_ids_refuse() -> Bool { let host = "" - match namecheap_read_hosts(body: response(result: result(domain: "gunb.ai", rows: concat(host, host))), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } + match namecheap_read_hosts(body: namecheap_api_response(result: result(domain: "gunb.ai", rows: concat(host, host))), domain: "gunb.ai") { NamecheapHostsRefused { reason: _ } => true _ => false } } test fn provider_result_fields_are_not_discarded() -> Bool { - match namecheap_read_hosts(body: response(result: ""), domain: "gunb.ai") { + match namecheap_read_hosts(body: namecheap_api_response(result: ""), domain: "gunb.ai") { NamecheapHostsObserved { domain: _, hosts: _, result_fields } => result_fields.any(f => f.name == "EmailType" && f.value == "MX") _ => false } diff --git a/dag/test/claim/namecheap_publication_witness_test.dag b/dag/test/claim/namecheap_publication_witness_test.dag index 648b96a9562..ff8b4ab98bf 100644 --- a/dag/test/claim/namecheap_publication_witness_test.dag +++ b/dag/test/claim/namecheap_publication_witness_test.dag @@ -3,13 +3,13 @@ module test.claim.namecheap_publication_witness_test import std.types { String, Secret, Bool } import extdeps.namecheap.client { namecheap_read_hosts_for_publication } import extdeps.namecheap.read_hosts { NamecheapHostsRead, NamecheapHostsObserved, NamecheapHostsRefused } -import test.claim.namecheap_hosts_witness_test { response } +import test.claim.namecheap_hosts_witness_test { namecheap_api_response } data fixture_key: Secret = "fixture-only-not-a-key&value" as Secret data fixture_key_wire: String = "fixture-only-not-a-key%26value" fn publication(result: String) -> NamecheapHostsRead { - namecheap_read_hosts_for_publication(body: response(result: result), domain: "gunb.ai", credential: fixture_key, key_wire: fixture_key_wire) + namecheap_read_hosts_for_publication(body: namecheap_api_response(result: result), domain: "gunb.ai", credential: fixture_key, key_wire: fixture_key_wire) } fn echo_result(address: String, extra_host: String, extra_result: String) -> String { join([" Date: Mon, 28 Sep 2026 12:23:47 +0000 Subject: [PATCH 08/24] Join host lexemes with the kernel join in the stage-verdicts test symbol_list_text declares the kernel String (bare String beside an import of v2.std.text { String }) and built it with v2.std.text string_join, a code-point sequence; the text wall refuses that return. The consumer is host concat, so the join is the host one. Co-Authored-By: Claude Opus 5.5 (1M context) --- ...self_host_candidate_generation_stage_verdicts_test.dag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag b/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag index eb5f2067359..b44222c6668 100644 --- a/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag +++ b/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag @@ -16,7 +16,7 @@ import v2.std.compilers.lexing { symbol_lexeme } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import v2.std.logic { Bool } import v2.std.node { Symbol } -import v2.std.text { String, string_join } +import v2.std.text { String } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly @@ -52,13 +52,13 @@ fn add_slice_stage_verdicts() -> CandidateGenerationStageVerdicts { } fn symbol_list_text(xs: List) -> String { - string_join( - fields: fold_list( + join( + fold_list( xs: xs, empty: Empty, cons: fn(acc, sym) { list_snoc_item(xs: acc, item: symbol_lexeme(sym: sym)) } ), - separator: ", " + ", " ) } From 52416096223ec79c76b155e60a11fbb1c1c5e564 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 14:40:21 +0000 Subject: [PATCH 09/24] text_boundary_identity_wall: record the spelling-collision receipts A user FreeMonoid record over a user Char admits host text on base and head (container-template recognition by spelling), and the current classifier fabricates admissions for a user List. The capability that closes the first is named; the second is why the classifier is being re-keyed on declaration identity. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/rung_drop/text_boundary_identity_wall.dag | 2 +- docs/design-rung-drops.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag index dc8d27cbee2..d1ba271e4c4 100644 --- a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag +++ b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag @@ -12,5 +12,5 @@ data text_boundary_identity_wall: RungDrop = RungDrop { standing: Standing, - declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } + declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 40fe28a972f..58cb1ec83fc 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -124,7 +124,7 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. -CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. ### Fabric CI evidence lane as a required merge block, and then as a lane at all — declared 2026-08-31 From 5285be2ff75149d66b4b70ddb13401e99cc062cb Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 28 Sep 2026 23:29:04 +0000 Subject: [PATCH 10/24] Text wall keyed on declaration identity; unidentified sides fall through to base (WIP, paused) Replaces the spelling-keyed classifier (review 72252) with v1.compiler.infer_env text_representation_by_identity: host text is the kernel String mint, a code-point sequence is std.algebra FreeMonoid over std.types Char by declaration identity (through aliases; refinements have their base's representation). Identity is read by declaring span, qualified name, or the declaring module's own facts -- never by re-resolving a spelling in the comparing site's scope. The relations carry the TypeEnv; the Rust emitter keeps its base provenance-keyed answer (byte-identical crate). Unidentified sides make no text verdict and base compatibility decides; each such site emits the advisory TextRepresentationUnidentifiedAtBoundary, the instrument for the restated drop (trigger: the resolver records declaration identity on every type reference; staged as node://adhoc-207dd6ac-6d2). Measured before the pause, against base 02360ee8f4 over the same tree: whole corpus 0 new blocking, 1 base refusal admitted (the ruling-B unfold at dag_arrow_lambda_witness_test:30); emitted self-host crate byte-identical; text_boundary_identity_wall_witness_test 21/21; regen first_generation_equal. NOT yet done: head-side base-vs-head claim comparison, and docs/design-rung-drops.md is not regenerated for the edited row. Co-Authored-By: Claude Opus 5.5 (1M context) --- ...epted_source_emits_uncompilable_target.dag | 2 +- .../rung_drop/text_boundary_identity_wall.dag | 2 +- dag/std/coercion.dag | 6 +- ...self_host_structural_text_witness_test.dag | 2 +- ...xt_boundary_identity_wall_witness_test.dag | 25 + src/v1/00_core.dag | 4 + src/v1/04_access.dag | 17 +- src/v1/04_emit_info.dag | 2 +- src/v1/04_env.dag | 230 +++++++- src/v1/04_infer.dag | 136 +++-- src/v1/04_types.dag | 59 +- src/v1/05_emit_rust.dag | 46 +- src/v1/coercion.dag | 96 +--- .../stage0/src/bin/infer_semantics_witness.rs | 27 +- src/v1/stage0/src/cli_run/compile_clean.rs | 7 + src/v1/stage0/src/std_coercion.rs | 3 + src/v1/stage0/src/v1_compiler_coercion.rs | 167 +----- src/v1/stage0/src/v1_compiler_emit_rust.rs | 58 +- src/v1/stage0/src/v1_compiler_infer.rs | 209 +++++--- src/v1/stage0/src/v1_compiler_infer_access.rs | 9 + .../stage0/src/v1_compiler_infer_emit_info.rs | 1 + src/v1/stage0/src/v1_compiler_infer_env.rs | 507 +++++++++++++++++- src/v1/stage0/src/v1_compiler_infer_types.rs | 46 +- src/v1/stage0/src/v1_std_core.rs | 10 + 24 files changed, 1201 insertions(+), 470 deletions(-) diff --git a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag index cb8dfc88c97..e0f477da610 100644 --- a/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag +++ b/dag/gunbc/recurring_failure_mode/accepted_source_emits_uncompilable_target.dag @@ -298,7 +298,7 @@ data accepted_source_emits_uncompilable_target: RecurringFailureMode = Recurring "OCCURRENCE, 2026-09-24 (wise-hawk-615, routed from stern-raven-24's native App Attest row gunbc#12251, whose emitted crate refused at rustc with 45 errors in three emitter classes and one runtime-library gap). Each was worked to its earliest unjustified boundary (DESIGN section 6b) rather than at the call site. (C) STRING ORDERING: `a < b` over Timestamp, an alias of String (extdeps.standards.rfc_5280 x509_validity_at, gunbc.auth.approval_capability utc_instant_before). Timestamp comparison needed no new typed ordering: the interpreter already orders Strings byte-lexicographically, so the defect was the emitter's. The boundary was the operand classifier v1.compiler.emit_rust rust_operand_realization_of_type, which filed the corpus String as OperandIdentityUnavailable although the type renderer realizes it as the host text carrier by is_host_text_carrier_type; ordering then refused by operator class while `==` on the same operand passed. Repair: std.operator_realization HostRealizationReason gains HostTextCarrier and the classifier reads the renderer's own predicate, so both answer one question once; is_string_comparison admits the four ordering operators through the same host-string seam as equality, and an ordering over an OPTIONAL String refuses at emission (the interpreter has no Null-vs-Str ordering; Option's None-first order would be fabricated). (A) PRESENT BINDING: `match opt { null => a o => b }` bound the whole Option in emitted code (E0308, E0609) while v1.compiler.infer narrows `o` to the present value. ALL THREE match renderings now read the checker's optional_scrutinee_binding_is_present over match_unguarded_absent_arm_index and emit `Some(o)` in exactly that arm, never re-deriving the narrowing: emit_typed_match_arm_strs for ordinary matches, and emit_typed_tco_match_arm for matches inside the tail-call lowering, which the native App Attest run surfaced separately (extdeps.standards.rfc_5280 read_extensions_list, the one error left after the first renderer was repaired); and emit_typed_match's String-literal re-emission (needs_string_from), where a review of this PR found the decision dropped -- there a string-literal arm over an optional scrutinee also emits `Some(ref __s)`, since a literal matches only a present value. (D) OCTETS: std.bytes bytes_octets and utf8_encode_bytes were interpreter-intercepted builtins whose .dag bodies were placeholders -- `[pure_dag_seam_unreachable()]`, a one-element list of `1 / 0`, and `s as Bytes`, a cast no Rust row realizes and which emitted a runtime panic. Both are now declared HostRealizedSeams (self-call bodies, std.primitive_projection rows, std.primitive_identity declarations with SourcePreservingOrder traversal facts, extdeps.languages.rust.emit rt_function_registry rows, v1.runtime_rust bodies); bytes_octets is typed List under the bounded_natural_arithmetic_evaluated_as_unbounded_int ruling, since UInt8 has no emitted realization that holds an octet and bytes_qualified_octets is the one boundary observing the range. Two further links surfaced by execution: a REALIZED seam's self-call was lowered by the name-keyed tail-call rewrite into a non-terminating loop (rust_host_seam_is_realized now exempts it, see host_seam_self_call_diverges_when_its_arm_is_absent), and rustc's deny-by-default unconditional_panic refused every crate reaching std.bytes because of pure_dag_seam_unreachable's constant `1 / 0`. The first cut relaxed that lint for all generated code; review 71098 refused it as widening a real check to admit one placeholder, and the placeholder was the earlier link: pure_dag_seam_unreachable is now a rostered HostRealizedSeam too, refusing in the interpreter with a typed error naming the seam and panicking by name in v1_rt, so the lint stays on. EVIDENCE: the fixture test.fixture.emitted_interpreted_parity.string_order_octets_present_binding exits 0 interpreted and requires the thirteen-field line `T F T F T 6 2962370309 97 0 989 ex none q` -- fields 1-5 string ordering, 6-7 octets, 8-9 the ordinary-match present binding, 10 the tail-call-match present binding, 11-13 the String-literal re-emission -- with a RED control that exits 1 printing the observed line; its emitted crate builds and runs to the same line. The enrolled claim module test.claim.emitter_string_order_present_binding_witness_test carries the emission half: host-string ordering over an alias, the named refusal for an ordering over a String? (which inference admits today, so the arm is reachable), and the present binding in all three match renderings. The integration consumer is `gunbc test //gunbc/instruments:native-app-attest` on gunbc#12251. RUNG unchanged at mitigatable for the class: required CI still neither emits-and-compiles a fixture closure nor executes integration targets, which is this row's existing trigger.", "OCCURRENCE, 2026-09-25 (loyal-boar-23, found by swift-bat-511 on native App Attest P2b): CALLING A LOCALLY BOUND FUNCTION VALUE. extdeps.apple.app_attest first_assertion_refusal folded a List AssertionRefusal?> and called the lambda-bound element `r()`; gunbc#12251 rewrote that one fold as a named AssertionStep sum, leaving the idiom refused everywhere else. The brief placed the defect in emit_rust's callee resolution; re-deriving the chain (DESIGN 6b) placed it three links earlier and found two more downstream. (1) INFERENCE, the earliest unjustified boundary: v1.compiler.infer child_type_node, the total read of a child position's type, returned a child's inferred slot whenever it had one, and an Arrow type node carries its RETURN type there (make_callable_type) -- so the element of List Int> was Int, a lambda parameter bound under it was not callable, and the call refused as \"function 'r' not found in scope\" in `gunbc run` as well as in emission. An Arrow child is now its own type -- landed independently by gunbc#12295 (receipted on arity_proxy_for_callability_misroutes_a_found_local) while this change was in review; this change consumes it rather than restating it. (2) INFERENCE, the list literal: a lambda's typed node carries its BODY type, and a list literal took its first element's type, so [fn() { none }] against List Int?> was typed List; where the declared element is an arrow and any element is a lambda the declared arrow is the element type, every element still judged against it. (3) EMISSION, two value positions of a lambda: a lambda literal in a list was a bare closure (E0308, every closure its own type), and is now the arrow carrier through the record-field row (rust_callable_field_value_wrap) bound at the rendered arrow by a typed let, not an `as` cast, which cuts the expectation typing Some(7) as Option (E0271); a let-bound lambda was `|x|` with no call site to type x from (E0282 at x.clone()) and is now emitted with typed parameters through emit_typed_collection_lambda, staying a bare closure so it still enters an impl Fn parameter. The call sites themselves (r(), f(v), g(v)) needed no change. EVIDENCE: test.fixture.emitted_interpreted_parity local_callable_value_calls exits 0 interpreted and its emitted crate, driven by a two-line harness over its library, prints the same line `6 8 6 7 9 0` with main returning ExitSuccess; with the expected line mutated both sides refuse (interpreter exit 1, emitted main ExitFailure carrying the observed line); on the pre-fix seed the fixture refuses at resolve. test.claim.local_callable_value_call_witness_test carries three inference controls (fold, unary fold and map lambda parameters over callable elements) and two emission controls (the list-literal carrier and the typed let), all five FAIL on the pre-fix seed and PASS here. RESIDUE, stated: an UNANNOTATED let-bound lambda (`let h = fn(x) { x * 2 }`) has no parameter type a Rust closure can be written at and still emits `|x: _|`-equivalent (E0282); a non-lambda element (a let-bound closure variable) in a List keeps its bare rendering. RUNG unchanged at mitigatable: required CI still neither emits-and-compiles a fixture closure nor runs its crate, which is this row's existing trigger.", "OCCURRENCE, 2026-09-24 (lively-koi-275, gunbc#12208 MQ-1): A FIELD BOUND UNDER A NESTED VARIANT PATTERN KEEPS ITS BOX. The `diagnostics` field of `v2.std.diagnostic` `Outcome` is a recursive Optional, so `v1.compiler.emit_rust` `needs_box_wrapping` boxes it. At a SINGLE-LEVEL arm `Accepted { value: v, diagnostics: d } =>` the emitter binds the field by `ref` and unboxes it (`let d = diagnostics.as_ref()`). Under a NESTED arm `Present { value: Accepted { value: facts, diagnostics: d } } =>` it bound the Box itself, and passing `d` to `v2.std.diagnostic` `diagnostics_merge` failed rustc with E0308 (expected `Option>`, found `Box>`) in the emitted `v2.compiler.infer`. The `.dag` was accepted with zero diagnostics, and only the non-required emit-build lane (`//gunbc/instruments:self-host`) saw it. The author-side repair split the match into two single-level arms, which the rest of the corpus already does by habit. That habit is an unstated workaround: it explains why the population is small and is not evidence that it is zero. The existing trigger of this row covers it: the emitted carrier comes from the declaration, and nested-pattern binders must go through the same unboxing join as single-level arms.", - "THE std.string_type COMPONENT IS DISCHARGED, 2026-09-28 (royal-newt-820, XL-0T ruling B): it now REFUSES AT CHECK in the shape it had, and its eight E0308 are gone because the shape no longer exists in the corpus. The link that was unjustified was neither of the two this row called justified -- it was the compatibility relation, which matched `String` against `String` by leaf spelling across two representations. The repair is one classifier, v1.compiler.coercion text_representation_of_type, read by the checker (corpus relation, call arguments, declared returns and data initializers, and builtin arguments, which had been admitted untyped) and by the Rust renderer. So a local `type String = FreeMonoid` (or a renamed `type Text = FreeMonoid`) handed to string_length/char_at/substring is a located refusal: test.claim.text_boundary_identity_wall_witness_test r_local_string_alias_into_kernel_primitive_refuses and r_renamed_text_alias_into_kernel_primitive_refuses. std.string_type's two functions are deleted (their eight callers use host String `<`, which is the same code-point order), and its `type String = FreeMonoid` stays as a declared frontier with no importer pending the seed kernel-names ruling; its 71 importers were nicknaming host text as that declaration and their imports are deleted, including src/v2/compiler/discovery_enumeration.dag, whose emitted closure no longer reaches std.string_type. THE TWO OPEN SHAPES THE ENTRY ABOVE ROUTED FOR A RULING were decided as B (two carriers, a declared crossing): a host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node keyed to the existing literal_homomorphism_rows row, and every other crossing refuses. The residue and its trigger are carried by gunbc.rung_drop text_boundary_identity_wall, not restated here.", + "THE std.string_type COMPONENT IS DISCHARGED, 2026-09-28 (royal-newt-820, XL-0T ruling B): it now REFUSES AT CHECK in the shape it had, and its eight E0308 are gone because the shape no longer exists in the corpus. The link that was unjustified was neither of the two this row called justified -- it was the compatibility relation, which matched `String` against `String` by leaf spelling across two representations. The repair is text representation decided by declaration identity (v1.compiler.infer_env text_representation_by_identity), read by the checker (corpus relation, call arguments, declared returns and data initializers, and builtin arguments, which had been admitted untyped). The Rust renderer keeps its provenance-keyed answer until its render paths carry the env, and is measured byte-identical to base on the self-host crate. So a local `type String = FreeMonoid` (or a renamed `type Text = FreeMonoid`) handed to string_length/char_at/substring is a located refusal: test.claim.text_boundary_identity_wall_witness_test r_local_string_alias_into_kernel_primitive_refuses and r_renamed_text_alias_into_kernel_primitive_refuses. std.string_type's two functions are deleted (their eight callers use host String `<`, which is the same code-point order), and its `type String = FreeMonoid` stays as a declared frontier with no importer pending the seed kernel-names ruling; its 71 importers were nicknaming host text as that declaration and their imports are deleted, including src/v2/compiler/discovery_enumeration.dag, whose emitted closure no longer reaches std.string_type. THE TWO OPEN SHAPES THE ENTRY ABOVE ROUTED FOR A RULING were decided as B (two carriers, a declared crossing): a host value at a code-point-sequence call argument takes the Unicode scalar unfold as a typed node keyed to the existing literal_homomorphism_rows row, and every other crossing refuses. The residue and its trigger are carried by gunbc.rung_drop text_boundary_identity_wall, not restated here.", ], diff --git a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag index d1ba271e4c4..a894423d52c 100644 --- a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag +++ b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag @@ -12,5 +12,5 @@ data text_boundary_identity_wall: RungDrop = RungDrop { standing: Standing, - declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here." } + declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT yet this authority: its renderers hold no TypeEnv, so it keeps its provenance-keyed answer, measured to render the self-host crate byte-identically to base, and unifying the two waits on the render paths carrying the env. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here.\n\nSTANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text." } } diff --git a/dag/std/coercion.dag b/dag/std/coercion.dag index c3d062e7167..92598e501fc 100644 --- a/dag/std/coercion.dag +++ b/dag/std/coercion.dag @@ -118,7 +118,11 @@ type TypeDeclarationProvenance // value of one representation at a position of the other is therefore a crossing with no route, // and the type-compatibility relation refuses it rather than matching the two by spelling. // NotText is every other type: this classification says nothing about it. -type TextRepresentation = HostText | CodePointSequence | NotText +type TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified + +// TextRepresentationUnidentified: the node's declaration could not be read by identity (no declaring +// span in the global index, no resolvable qualified name, an alias chain that cycles or exceeds its +// bound). It is never read as NotText: a text boundary refuses a value it cannot identify. // WHICH STATE A TYPE REFERENCE IS IN WHEN ITS DECLARATION IS ASKED FOR -- the split of the one arm // the reference-site read used to answer for two states. v1.std.core type_reference_provenance diff --git a/dag/test/claim/self_host_structural_text_witness_test.dag b/dag/test/claim/self_host_structural_text_witness_test.dag index 875597b75bf..be00ac6bfc5 100644 --- a/dag/test/claim/self_host_structural_text_witness_test.dag +++ b/dag/test/claim/self_host_structural_text_witness_test.dag @@ -126,7 +126,7 @@ test fn w_literal_at_symbol_boundary_still_emits_host_string() -> Bool { // row used to assert the GAP: a non-literal kernel-String value (the kernel concat builtin's result) // at an exact qualified v2.std.text.String return boundary was accepted with no homomorphism and no // refusal. The wall now keys on text representation rather than leaf spelling -// (v1.compiler.coercion text_representation_of_type), and a declared return carries no unfold, so +// (v1.compiler.infer_env text_representation_by_identity), and a declared return carries no unfold, so // the same fixture is a blocking refusal. It stays enrolled as the discriminating control that the // wall is real; its paired positive controls are the literal rows above and // test.claim.text_boundary_identity_wall_witness_test. diff --git a/dag/test/claim/text_boundary_identity_wall_witness_test.dag b/dag/test/claim/text_boundary_identity_wall_witness_test.dag index 5c776554aa1..c146f1ec6bd 100644 --- a/dag/test/claim/text_boundary_identity_wall_witness_test.dag +++ b/dag/test/claim/text_boundary_identity_wall_witness_test.dag @@ -174,3 +174,28 @@ test fn e_bare_string_parameter_beside_a_text_import_takes_a_host_literal() -> B ["vec![46]"] ) } + +// SPELLING COLLISIONS -- the classifier decides by DECLARATION, so a user type that merely shares a +// spelling is not text (review 72252). Each fixture declares its own `type Char { c: Int }`; a +// spelling-keyed classifier read `List` as a code-point sequence and unfolded host text into +// a list of user records, ADMITTING what the base compiler refused. +test fn x_host_value_at_a_list_of_a_user_char_refuses() -> Bool { + refuses(source: "module probe_collision_a\ntype Char { c: Int }\nfn take(xs: List) -> Int { 0 }\nfn f(s: String) -> Int { take(xs: s) }\n") +} + +test fn x_host_literal_at_a_list_of_a_user_char_refuses() -> Bool { + refuses(source: "module probe_collision_b\ntype Char { c: Int }\nfn take(xs: List) -> Int { 0 }\nfn f() -> Int { take(xs: \"hi\") }\n") +} + + +// REFINEMENT PEELING (v1.compiler.infer_env is_where_refinement_node): a refinement has its base's +// representation, so std.types NonEmptyStr (`String where non_empty`) at a code-point formal +// unfolds exactly as a String does. +test fn u_refined_host_value_at_qualified_text_parameter_unfolds() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.text_unfold_refined_probe\nimport std.types { NonEmptyStr }\nimport v2.std.text { string_is_empty }\nfn take(t: v2.std.text.String) -> Int { 0 }\nfn host_into(s: NonEmptyStr) -> Int { take(t: s) }\n", + "src/test_claim_text_unfold_refined_probe.rs", + [".chars().map(|c| c as i64)"], + ["take(s)", "take(s.clone())"] + ) +} diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 6903c6df7cf..798704b6ba4 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -399,6 +399,7 @@ type CompilerDiagnostic | MethodExistenceUndecided { method: String, receiver_type: String, span: SourceSpan } | MethodExistenceFrontierAdmitted { method: String, receiver_type: String, trigger: String, span: SourceSpan } | ReceiverTypeUnestablished { method: String, span: SourceSpan } + | TextRepresentationUnidentifiedAtBoundary { position: String, span: SourceSpan } | AlgebraApplicationEvidenceUnavailable { receiver_type: String, argument_index: Int, span: SourceSpan } | FrontierOccurrenceBudgetExceeded { method: String, receiver_type: String, declared: Int, observed: Int, span: SourceSpan } | TestCodeReferenced { referrer: String, target: String, span: SourceSpan } @@ -628,6 +629,7 @@ fn diagnostic_to_span(d: CompilerDiagnostic) -> SourceSpan { MethodExistenceUndecided { method: _, receiver_type: _, span: s } => s MethodExistenceFrontierAdmitted { method: _, receiver_type: _, trigger: _, span: s } => s ReceiverTypeUnestablished { method: _, span: s } => s + TextRepresentationUnidentifiedAtBoundary { position: _, span: s } => s AlgebraApplicationEvidenceUnavailable { receiver_type: _, argument_index: _, span: s } => s FrontierOccurrenceBudgetExceeded { method: _, receiver_type: _, declared: _, observed: _, span: s } => s TestCodeReferenced { referrer: _, target: _, span: s } => s @@ -711,6 +713,7 @@ fn diagnostic_to_message(d: CompilerDiagnostic) -> String { MethodExistenceUndecided { method: m, receiver_type: t, span: _ } => concat("method '", m, "' cannot be resolved: receiver type '", t, "' establishes no method surface, so the method's existence is not established and no declared frontier row admits it") MethodExistenceFrontierAdmitted { method: m, receiver_type: t, trigger: tr, span: _ } => concat("method '", m, "' on receiver type '", t, "' is admitted by a declared unresolved-method frontier row; dissolves on: ", tr) AlgebraApplicationEvidenceUnavailable { receiver_type: t, argument_index: i, span: _ } => concat("algebra receiver application evidence unavailable for '", t, "' at argument ", to_string(value: i), ": structural members are not type arguments") + TextRepresentationUnidentifiedAtBoundary { position: p, span: _ } => concat(concat("text boundary unjudged at ", p), ": one side is identified text and the other side's type carries no declaration identity the compiler can read, so the text wall makes no verdict here and base type compatibility decides (gunbc.rung_drop text_boundary_identity_wall; trigger: the resolver records declaration identity on every type reference)") ReceiverTypeUnestablished { method: _, span: _ } => "the receiver's own type was never established, so nothing is known about the method's existence here; this is an upstream type-propagation deficit, not a fact about the method" FrontierOccurrenceBudgetExceeded { method: m, receiver_type: t, declared: d, observed: o, span: _ } => concat(concat(concat("the declared frontier row for '", m), concat("' on receiver type '", t)), concat(concat("' no longer matches what this module contains: the row declares ", to_string(value: d)), concat(" occurrence(s) and ", to_string(value: o))), " were observed here. If MORE were observed, a new unresolved call has appeared and the receiver's type should be established rather than the count raised. If FEWER were observed, the deficit has partly dissolved and the row must be lowered or deleted so the ratchet keeps its new ground. The count is an equality, not a ceiling, in both directions.") @@ -873,6 +876,7 @@ fn diagnostic_disposition(d: CompilerDiagnostic) -> DiagnosticDisposition { MethodExistenceFrontierAdmitted { method: _, receiver_type: _, trigger: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } AlgebraApplicationEvidenceUnavailable { receiver_type: _, argument_index: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } ReceiverTypeUnestablished { method: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } + TextRepresentationUnidentifiedAtBoundary { position: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } FrontierOccurrenceBudgetExceeded { method: _, receiver_type: _, declared: _, observed: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } TestCodeReferenced { referrer: _, target: _, span: _ } => DiagnosticDisposition { severity: SeverityError, gate: GateBlocking } TestCodeReferenceAdmitted { referrer: _, target: _, span: _ } => DiagnosticDisposition { severity: SeverityNonError, gate: GateAdvisoryTypecheck } diff --git a/src/v1/04_access.dag b/src/v1/04_access.dag index 6a2bec8beb8..1341fd16060 100644 --- a/src/v1/04_access.dag +++ b/src/v1/04_access.dag @@ -1,6 +1,7 @@ module v1.compiler.infer_access import std.types { SourceSpan, is_ordered_element_collection } +import v1.compiler.infer_env { TypeEnv } import v1.std.core { Node, NewlineIndex, authored_name_at, @@ -66,11 +67,11 @@ fn keyed_collection_parts(n: Node, source_indices: Map) -> } } -fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, module_name: String, source_indices: Map) -> AccessCheckResultNode { +fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, module_name: String, source_indices: Map, env: TypeEnv?) -> AccessCheckResultNode { let normed = normalize_access_type_node(n: base_type) let normed_index = normalize_access_type_node(n: index_type) - let base_is_string = node_type_equals(left: normed, right: string_type, source_indices: source_indices) - let index_is_int = node_type_equals(left: normed_index, right: int_type, source_indices: source_indices) + let base_is_string = node_type_equals(left: normed, right: string_type, source_indices: source_indices, env: env) + let index_is_int = node_type_equals(left: normed_index, right: int_type, source_indices: source_indices, env: env) if base_is_string { let diags = if index_is_int { [] } else { [access_error(message: "string index requires an Int index", span: span, module_name: module_name)] } @@ -78,7 +79,7 @@ fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, } else { match keyed_collection_parts(n: normed, source_indices: source_indices) { Present { value: parts } => - let key_diags = if node_type_equals(left: parts.key_type, right: normed_index, source_indices: source_indices) { [] } + let key_diags = if node_type_equals(left: parts.key_type, right: normed_index, source_indices: source_indices, env: env) { [] } else { [access_error(message: "keyed collection index key type does not match the collection key type", span: span, module_name: module_name)] } access_result(inferred: with_optional_cardinality(n: parts.value_type), diagnostics: key_diags, span: span, fallback_message: "invalid keyed collection index access") Absent => @@ -96,18 +97,18 @@ fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, } } -fn check_slice_access_node(base_type: Node, start_type: Node, end_type: Node, span: SourceSpan, module_name: String, source_indices: Map) -> AccessCheckResultNode { +fn check_slice_access_node(base_type: Node, start_type: Node, end_type: Node, span: SourceSpan, module_name: String, source_indices: Map, env: TypeEnv?) -> AccessCheckResultNode { let normed_base = normalize_access_type_node(n: base_type) - let base_is_string = node_type_equals(left: normed_base, right: string_type, source_indices: source_indices) + let base_is_string = node_type_equals(left: normed_base, right: string_type, source_indices: source_indices, env: env) let base_is_list = is_ordered_element_collection(name: authored_name_at(source_indices: source_indices, node: normed_base)) && node_is_element_collection(n: normed_base, source_indices: source_indices) let base_diags = if base_is_string || base_is_list { [] } else { [access_error(message: "slice is only supported for String and list values", span: span, module_name: module_name)] } let normed_start = normalize_access_type_node(n: start_type) - let start_diags = if node_type_equals(left: normed_start, right: int_type, source_indices: source_indices) { [] } + let start_diags = if node_type_equals(left: normed_start, right: int_type, source_indices: source_indices, env: env) { [] } else { [access_error(message: "slice start requires an Int index", span: span, module_name: module_name)] } let normed_end = normalize_access_type_node(n: end_type) - let end_diags = if node_type_equals(left: normed_end, right: int_type, source_indices: source_indices) { [] } + let end_diags = if node_type_equals(left: normed_end, right: int_type, source_indices: source_indices, env: env) { [] } else { [access_error(message: "slice end requires an Int index", span: span, module_name: module_name)] } let all_diags = concat(base_diags, start_diags, end_diags) let slice_result_type = if base_is_string { string_type } diff --git a/src/v1/04_emit_info.dag b/src/v1/04_emit_info.dag index 6c8fbe27f18..6a1a72e028d 100644 --- a/src/v1/04_emit_info.dag +++ b/src/v1/04_emit_info.dag @@ -425,7 +425,7 @@ fn enum_field_present_in_all_variants(variants: List, field_name: String, fn enum_field_type_consistent(variants: List, field_name: String, expected: Node, source_indices: Map) -> Bool { variants |> all(variant => match find_child_named(n: variant, name: field_name, source_indices: source_indices) { - Present { value: field_child } => node_type_equals(left: child_type_node(ch:field_child), right: expected, source_indices: source_indices) + Present { value: field_child } => node_type_equals(left: child_type_node(ch:field_child), right: expected, source_indices: source_indices, env: none) Absent => false } ) diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index 37b4b670881..da16db2d967 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -2,12 +2,12 @@ module v1.compiler.infer_env import std.occurrence_identity { OccurrenceSynthetic } -import v1.std.core { Unmarked, Node, NewlineIndex, InternTable, Resolved, qualified_last_segment, empty_intern_table, merge_intern_tables, intern, intern_find, intern_str, source_text_at, authored_name_at, find_child_named, Cardinality, Connective, ExprData, InferredNode, kernel_span, declaration_provenance_of, module_path_segments, param_node_name_at, param_node_type_expr, CompilerDiagnostic, AmbiguousReference, UnresolvedType } +import v1.std.core { Unmarked, TypeVariable, CompilerError, Node, NewlineIndex, InternTable, Resolved, qualified_last_segment, empty_intern_table, merge_intern_tables, intern, intern_find, intern_str, source_text_at, authored_name_at, find_child_named, Cardinality, Connective, NoConnective, ExprData, InferredNode, kernel_span, declaration_provenance_of, module_path_segments, param_node_name_at, param_node_type_expr, CompilerDiagnostic, AmbiguousReference, UnresolvedType } import v1.compiler.type_head_exposure { TypeHeadExposure } import std.types { SourceSpan, is_kernel_type } -import std.coercion { TypeDeclarationProvenance, DeclarationIdentityAbsent } -import std.decl_ref { decl_ref, DeclarationRef } +import std.coercion { TypeDeclarationProvenance, DeclarationIdentityAbsent, KernelMinted, CorpusDeclared, TextRepresentation, HostText, CodePointSequence, NotText, TextRepresentationUnidentified } +import std.decl_ref { decl_ref, DeclarationRef, declaration_ref_eq, declaration_ref_display_key } import std.operator_realization { OperandDeclaration } import std.induction { InductiveField, RecursionShape, DirectRecursion, ListRecursion, OptionalRecursion, SetRecursion, MapValueRecursion, SubValueRelation, SubValueUnknown, PreservedValue } @@ -1601,6 +1601,230 @@ fn declaration_provenance_of_ref(d: DeclarationRef, env: TypeEnv) -> TypeDeclara // census (module prefix must equal the census row's declarer); a BARE authored name answers none // here — no prefix, and the occurrence span matches no declaration — so every unqualified // destination takes exactly the hops it took before this arm existed. +// TEXT REPRESENTATION BY DECLARATION IDENTITY (gunbc.rung_drop text_boundary_identity_wall; XL-0T +// ruling B; review 72252). A type node's representation is decided by WHICH DECLARATION it resolves +// to, never by its spelling: +// - HostText: the kernel String mint, identified by declaration_provenance_of (the exact kernel_span +// constructor test), on the node or on its resolved node. +// - CodePointSequence: the declaration std.algebra FreeMonoid applied to the declaration std.types +// Char, reached directly or through a chain of transparent aliases. +// - NotText: any other identified declaration. +// - TextRepresentationUnidentified: no identity could be read. Never defaulted. +// IDENTITY IS READ IN THE AUTHORING MODULE'S TERMS, NEVER RE-RESOLVED IN THE CONSUMER'S SCOPE: only by +// the node's declaring span in the corpus-wide index or by its qualified name +// (declaration_ref_of_type_node), and on the node the resolver already produced in the declaring +// module (its inferred Resolved node). The by-name fallbacks of type_reference_declaration_ref +// (lookup_type_for / lookup_type_by_name in the caller's env) are deliberately NOT used: re-reading +// a callee's bare `String` in a foreign caller's scope is the caller-re-resolution escape. +fn text_identity_std_algebra_free_monoid() -> DeclarationRef { decl_ref(module_path: "std.algebra", decl_name: "FreeMonoid") } +fn text_identity_std_types_char() -> DeclarationRef { decl_ref(module_path: "std.types", decl_name: "Char") } + +// A kernel mint that names no corpus declaration (Int, Bool, the seed's Hash, ...) is identified by +// the kernel itself as a distinct nominal type: not text. It is consulted only AFTER the identity +// read, so a stand-in the resolver minted for a qualified corpus reference is still read as the +// declaration it names. +fn node_is_kernel_mint(n: Node) -> Bool { + match declaration_provenance_of(item: n) { + KernelMinted { minted_name: _ } => true + CorpusDeclared { decl_file: _ } => false + DeclarationIdentityAbsent => false + } +} + +fn node_is_kernel_mint_named(n: Node, name: String) -> Bool { + match declaration_provenance_of(item: n) { + KernelMinted { minted_name: m } => m == name + CorpusDeclared { decl_file: _ } => false + DeclarationIdentityAbsent => false + } +} + +fn authored_declaration_of_type_node(n: Node, env: TypeEnv) -> DeclarationRef? { + match declaration_ref_of_type_node(rt: n, source_indices: env.source_indices, env: env) { + Present { value: d } => Present { value: d } + Absent => + match n.inferred { + Present { value: Resolved { node: r } } => declaration_ref_of_type_node(rt: r, source_indices: env.source_indices, env: env) + _ => none + } + } +} + +fn declaration_binding_of_ref(d: DeclarationRef, env: TypeEnv) -> TypeBinding? { + match map_get(env.symbol_index.global_bare, d.decl_name as String) { + Present { value: GlobalBareUniqueBinding { module_path: mp, binding: b } } => + if mp == (d.module_path as String) { Present { value: b } } else { none } + Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => + match cands |> filter(c => c.module_path == (d.module_path as String)) |> first { + Present { value: c } => Present { value: c.binding } + Absent => none + } + Absent => none + } +} + +fn text_element_is_char(n: Node, env: TypeEnv, authoring: String?) -> TextRepresentation { + match n.children |> first { + Absent => TextRepresentationUnidentified + Present { value: ch } => + let el = match ch.inferred { Present { value: Resolved { node: r } } => r _ => ch } + let reading = match authored_declaration_of_type_node(n: el, env: env) { + Present { value: e } => AuthoredReferenceIsDeclaration { declaration: e } + Absent => match authoring { + Present { value: m } => authored_reference_reading(n: el, authoring_module: m, env: env) + Absent => AuthoredReferenceUnread + } + } + match reading { + AuthoredReferenceIsDeclaration { declaration: e } => if declaration_ref_eq(a: e, b: text_identity_std_types_char()) { CodePointSequence } else { NotText } + AuthoredReferenceIsKernel { name: _ } => NotText + AuthoredReferenceUnread => TextRepresentationUnidentified + } + } +} + +fn text_representation_by_identity(n: Node, env: TypeEnv) -> TextRepresentation { + text_representation_by_identity_bounded(n: n, env: env, fuel: 16, visited: [], authoring: none) +} + +// A BARE REFERENCE INSIDE A DECLARATION'S BODY IS READ IN ITS AUTHORING MODULE'S TERMS -- the +// module the chase entered through (the declaration binding's module path), never the consumer's +// scope. With only corpus-wide facts: the authoring module's OWN declaration of the name if it has +// one; otherwise the kernel, if the spelling is a kernel type (imports never override a kernel +// name); otherwise the corpus's one declaration of the name when it has exactly one; otherwise +// unidentified. +type AuthoredReferenceReading + = AuthoredReferenceIsKernel { name: String } + | AuthoredReferenceIsDeclaration { declaration: DeclarationRef } + | AuthoredReferenceUnread + +fn authored_reference_reading(n: Node, authoring_module: String, env: TypeEnv) -> AuthoredReferenceReading { + let nm = qualified_last_segment(name: authored_name_at(source_indices: env.source_indices, node: n)) + if nm == "" { AuthoredReferenceUnread } + else { + let local = match map_get(env.symbol_index.global_bare, nm) { + Present { value: GlobalBareUniqueBinding { module_path: mp, binding: _ } } => mp == authoring_module + Present { value: GlobalBareAmbiguousBinding { candidates: cands } } => cands |> any(c => c.module_path == authoring_module) + Absent => false + } + if local { AuthoredReferenceIsDeclaration { declaration: decl_ref(module_path: authoring_module, decl_name: nm) } } + else if is_kernel_type(name: nm) { AuthoredReferenceIsKernel { name: nm } } + else { + match map_get(env.symbol_index.global_bare, nm) { + Present { value: GlobalBareUniqueBinding { module_path: mp, binding: _ } } => AuthoredReferenceIsDeclaration { declaration: decl_ref(module_path: mp, decl_name: nm) } + _ => AuthoredReferenceUnread + } + } + } +} + +// A declaration with no body, or whose resolved form is itself, is opaque: a type of its own +// (v2.std.node Symbol), and not text. A generic type variable is a slot, not a declaration, and a +// compiler-error type already carries its own diagnostic, so neither is text and neither is refused +// here a second time. +fn text_representation_by_identity_bounded(n: Node, env: TypeEnv, fuel: Int, visited: List, authoring: String?) -> TextRepresentation { + let resolved = match n.inferred { Present { value: Resolved { node: r } } => Present { value: r } _ => none } + let kernel_string = node_is_kernel_mint_named(n: n, name: "String") || match resolved { + Present { value: r } => node_is_kernel_mint_named(n: r, name: "String") + Absent => false + } + let generic_or_error = match n.inferred { + Present { value: TypeVariable { id: _ } } => true + Present { value: CompilerError { message: _, span: _ } } => true + _ => false + } + let kernel_other = node_is_kernel_mint(n: n) || match resolved { + Present { value: r } => node_is_kernel_mint(n: r) + Absent => false + } + if kernel_string { HostText } + else if generic_or_error { NotText } + else if fuel <= 0 { TextRepresentationUnidentified } + else if is_where_refinement_node(n: n) { + let base_authoring = match authored_declaration_of_type_node(n: n, env: env) { + Present { value: own } => Present { value: own.module_path as String } + Absent => authoring + } + match n.children |> first { + Present { value: base } => text_representation_by_identity_bounded(n: base, env: env, fuel: fuel - 1, visited: visited, authoring: base_authoring) + Absent => TextRepresentationUnidentified + } + } + else { + let identified = match authored_declaration_of_type_node(n: n, env: env) { + Present { value: d } => AuthoredReferenceIsDeclaration { declaration: d } + Absent => match authoring { + Present { value: m } => authored_reference_reading(n: n, authoring_module: m, env: env) + Absent => AuthoredReferenceUnread + } + } + match identified { + AuthoredReferenceUnread => if kernel_other { NotText } else { TextRepresentationUnidentified } + AuthoredReferenceIsKernel { name: k } => if k == "String" { HostText } else { NotText } + AuthoredReferenceIsDeclaration { declaration: d } => + if declaration_ref_eq(a: d, b: text_identity_std_algebra_free_monoid()) { + text_element_is_char(n: n, env: env, authoring: authoring) + } else { + let key = declaration_ref_display_key(ref: d) + if visited |> any(v => v == key) { TextRepresentationUnidentified } + else { + match declaration_binding_of_ref(d: d, env: env) { + Absent => TextRepresentationUnidentified + Present { value: b } => + let decl = b.resolved + let is_alias = decl.connective == NoConnective && (decl.children |> count) == 0 + if is_where_refinement_node(n: decl) { + text_representation_by_identity_bounded(n: decl, env: env, fuel: fuel - 1, visited: concat(visited, [key]), authoring: Present { value: d.module_path as String }) + } + else if !is_alias { NotText } + else { + match decl.inferred { + Present { value: Resolved { node: target } } => + let target_is_itself = match authored_declaration_of_type_node(n: target, env: env) { + Present { value: t } => declaration_ref_eq(a: t, b: d) + Absent => false + } + if target_is_itself { NotText } + else { text_representation_by_identity_bounded(n: target, env: env, fuel: fuel - 1, visited: concat(visited, [key]), authoring: Present { value: d.module_path as String }) } + _ => NotText + } + } + } + } + } + } + } +} + +// REFINEMENT PEELING IS A CLASSIFICATION RULE (not a reader fallback): a where-refinement +// `T where p` has T's representation -- a refinement narrows which values inhabit a type, it does +// not change how they are represented. So std.types NonEmptyStr (`String where non_empty`) is host +// text exactly as String is, and a NonEmptyStr at a code-point formal unfolds or refuses exactly +// like a String would (test.claim.text_boundary_identity_wall_witness_test's refinement rows). +fn is_where_refinement_node(n: Node) -> Bool { + n.connective == Conj && n.type_annotation != none && (n.children |> count) == 1 +} + +fn text_representation_is_text_arm(r: TextRepresentation) -> Bool { + match r { HostText => true CodePointSequence => true NotText => false TextRepresentationUnidentified => false } +} + +// A CROSSING: both sides are identified, one as each representation. An UNIDENTIFIED side makes no +// judgment here -- measured, most v1 type references carry no readable declaration identity yet, so +// refusing them refused thousands of correct host-text sites; that population is a declared drop +// (gunbc.rung_drop text_boundary_identity_wall) whose trigger is the resolver recording declaration +// identity on every type reference. +fn text_crossing_by_identity(left: Node, right: Node, env: TypeEnv) -> Bool { + let l = text_representation_by_identity(n: left, env: env) + let r = text_representation_by_identity(n: right, env: env) + match l { + HostText => match r { CodePointSequence => true TextRepresentationUnidentified => false HostText => false NotText => false } + CodePointSequence => match r { HostText => true TextRepresentationUnidentified => false CodePointSequence => false NotText => false } + NotText => false + TextRepresentationUnidentified => false + } +} + fn type_reference_declaration_ref(n: Node, source_indices: Map, env: TypeEnv) -> DeclarationRef? { let from_authored = declaration_ref_of_type_node(rt: n, source_indices: source_indices, env: env) if from_authored != none { return from_authored } diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index e6dd72a0d44..4debca787e8 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -17,7 +17,7 @@ import std.literal_elaboration { elaborate_literal_at, literal_source_kind_of, literal_elaboration_refusal_message } import gunbc.structural_realization_bindings { literal_homomorphism_rows } -import std.coercion { dag_can_cast, is_dag_cast_domain_type, dag_cast_requires_proof, SubtractionRefinementAmbiguous, HostText, CodePointSequence, NotText, KernelMinted, CorpusDeclared, DeclarationIdentityAbsent } +import std.coercion { dag_can_cast, is_dag_cast_domain_type, dag_cast_requires_proof, SubtractionRefinementAmbiguous, HostText, CodePointSequence, NotText, TextRepresentationUnidentified, KernelMinted, CorpusDeclared, DeclarationIdentityAbsent } import v1.compiler.type_head_exposure { TypeHeadView, KernelScalarHead, ApplicationHead, ProductHead, CoproductHead, CallableHead, TypeHeadExposure, ExposedTypeHead, OpaqueTypeHead, StuckTypeHead, MalformedApplicationHead, @@ -51,7 +51,7 @@ import v1.std.core { Unmarked, ErrorNode, make_error_node, is_error_diagnostic, is_interpreter_blocking_diagnostic, diagnostic_to_span, AlgebraApplicationEvidenceUnavailable, FrontierOccurrenceKey, diagnostic_frontier_occurrence_key, - InternalError, TypeMismatch, TypeParameterInValuePosition, VariantCollision, SoleConstructorViolation, OptionalCastNotEliminated, BareNoneNotAdmittedByFieldType, ConstructorCallAdmissionRefused, UnresolvedType, FieldNotFound, MethodNotFound, MethodExistenceUndecided, MethodExistenceFrontierAdmitted, ReceiverTypeUnestablished, FrontierOccurrenceBudgetExceeded, MissingField, AmbiguousReference, ServiceConfigReferenceJudgmentDeferred, UnlistedVariantValueUse, + InternalError, TypeMismatch, TypeParameterInValuePosition, TextRepresentationUnidentifiedAtBoundary, VariantCollision, SoleConstructorViolation, OptionalCastNotEliminated, BareNoneNotAdmittedByFieldType, ConstructorCallAdmissionRefused, UnresolvedType, FieldNotFound, MethodNotFound, MethodExistenceUndecided, MethodExistenceFrontierAdmitted, ReceiverTypeUnestablished, FrontierOccurrenceBudgetExceeded, MissingField, AmbiguousReference, ServiceConfigReferenceJudgmentDeferred, UnlistedVariantValueUse, CallArgumentNameUnknown, CallPositionalSurplus, CallPositionalDeficit, CallNamedArgOnFunctionValue, CallArgumentDuplicate, EqualityOnFunctionMember, EqualityMemberUnjudgeable, TypeArgumentArityMismatch, @@ -116,8 +116,9 @@ import v1.compiler.infer_method { builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, builtin_host_text_param_names, builtin_param_names } import v1.compiler.infer_cycle { detect_type_cycles_kahn } -import v1.compiler.coercion { provenance_realizes_natively, text_representation_of_type, text_representations_cross } +import v1.compiler.coercion { provenance_realizes_natively } import v1.compiler.infer_env { + text_representation_by_identity, text_crossing_by_identity, TypeEnv, TypeBinding, TypeEnvCache, empty_type_env_cache, merge_type_env_cache, merge_type_env_cache_guarded, GuardedTypeEnvCacheMerge, TypeEnvCacheMergeConflict, str_bindings_from_bindings, @@ -1469,16 +1470,25 @@ fn declared_type_kernel_inhabitance_mismatch_here_or_at_element( // not a new relation (gunbc.recurring_failure_mode declared_type_wall_keyed_on_the_value_being_a_kernel, // specimen four). // IN declared_type_conformance_diags, A TEXT CROSSING IS PROVEN, NOT UNJUDGED: both sides' representations come from the one -// classifier (v1.compiler.coercion text_representation_of_type), so a host-text value produced +// classifier (v1.compiler.infer_env text_representation_by_identity), so a host-text value produced // where a code-point sequence is declared -- or the reverse -- is a real difference with no // alias or brand between the two, and these positions carry no unfold (only a call argument // does, v1.compiler.infer host_text_unfolded_at_code_point_boundary). gunbc.rung_drop // text_boundary_identity_wall. fn declared_type_conformance_diags(declared: Node, produced: Node, span: SourceSpan, scope: InferScope) -> List { + concat( + text_unjudged_advisories( + a: text_representation_by_identity(n: declared, env: scope.type_env), + b: text_representation_by_identity(n: produced, env: scope.type_env), + position: "a declared type", span: span, module_name: scope.module_name), + declared_type_conformance_diags_core(declared: declared, produced: produced, span: span, scope: scope)) +} + +fn declared_type_conformance_diags_core(declared: Node, produced: Node, span: SourceSpan, scope: InferScope) -> List { let si = scope.type_env.source_indices let both_ground = conformance_ground_type(n: declared, source_indices: si) && conformance_ground_type(n: produced, source_indices: si) - if text_representations_cross(left: declared, right: produced, source_indices: si) { + if text_crossing_by_identity(left: declared, right: produced, env: scope.type_env) { [type_mismatch_error( expected: node_type_shape(n: declared, source_indices: si), got: node_type_shape(n: produced, source_indices: si), @@ -1531,7 +1541,7 @@ fn declared_type_conformance_diags(declared: Node, produced: Node, span: SourceS )] } else if !both_ground { [] } - else if node_type_compatible(left: declared, right: produced, source_indices: si) { [] } + else if node_type_compatible(left: declared, right: produced, source_indices: si, env: Present { value: scope.type_env }) { [] } else { [type_mismatch_error( expected: node_type_shape(n: declared, source_indices: si), @@ -1873,10 +1883,10 @@ fn set_element_type_is_concrete(elem: Node) -> Bool { } } -fn set_element_types_mismatch(recv_elem: Node, operand_elem: Node, source_indices: Map) -> Bool { +fn set_element_types_mismatch(recv_elem: Node, operand_elem: Node, source_indices: Map, env: TypeEnv?) -> Bool { set_element_type_is_concrete(elem: recv_elem) && set_element_type_is_concrete(elem: operand_elem) - && !node_type_compatible(left: recv_elem, right: operand_elem, source_indices: source_indices) + && !node_type_compatible(left: recv_elem, right: operand_elem, source_indices: source_indices, env: env) } fn node_admits_list_literal(n: Node, source_indices: Map) -> Bool { @@ -2823,7 +2833,7 @@ fn match_arm_types_are_proven_disjoint(unified_arm_type: Node, arm_type: Node, s conformance_ground_type(n: unified_arm_type, source_indices: source_indices) && conformance_ground_type(n: arm_type, source_indices: source_indices) && node_type_compatible( - left: unified_arm_type, right: arm_type, source_indices: source_indices) == false + left: unified_arm_type, right: arm_type, source_indices: source_indices, env: Present { value: scope.type_env }) == false } } @@ -5353,7 +5363,7 @@ fn callable_component_ground_mismatch( || conformance_ground_type(n: instantiated, source_indices: source_indices) } else if conformance_ground_type(n: formal_part, source_indices: source_indices) && conformance_ground_type(n: instantiated, source_indices: source_indices) { - !node_type_compatible(left: formal_part, right: instantiated, source_indices: source_indices) + !node_type_compatible(left: formal_part, right: instantiated, source_indices: source_indices, env: none) } else { false } } } @@ -5507,7 +5517,7 @@ fn direct_call_arg_type_mismatch( else if direct_call_formal_has_unbound_type_variable(n: substitution_basis) || direct_call_formal_has_unbound_type_variable(n: actual) { false } else if type_node_is_callable(n: formal) || type_node_is_callable(n: actual) { false } else { - text_representations_cross(left: formal, right: actual, source_indices: source_indices) + text_crossing_by_identity(left: formal, right: actual, env: type_env) || nominal_call_arg_brand_mismatch(formal: formal, actual: actual, type_env: type_env, source_indices: source_indices) || container_element_nominal_brand_mismatch( formal: formal, actual: actual, type_env: type_env, module_name: module_name, source_indices: source_indices) @@ -5808,7 +5818,11 @@ data direct_call_shape_wall_note: String = "WALL (DESIGN §5) — the compile se // Record-literal actuals skip this path; handoff lives in // direct_call_structured_application_mismatch_diags (+ resolved-type legacy checks there). -// A VALUE'S TEXT REPRESENTATION IS READ FROM BOTH OF ITS TYPE VIEWS. The resolved type and its +// A VALUE'S TEXT REPRESENTATION IS ITS RESOLVED TYPE'S DECLARATION IDENTITY +// (v1.compiler.infer_env text_representation_by_identity). It is NOT re-read through +// peel_nominal_alias_identity: that peel looks the spelling up by name in THIS scope, which is the +// caller-re-resolution escape. The history below records why an earlier revision read two views. +// A VALUE'S TEXT REPRESENTATION WAS READ FROM BOTH OF ITS TYPE VIEWS. The resolved type and its // alias-peeled form each lose the structure in one known case: a qualified v2.std.text.String value's // resolved node still reads as the spelling String (so only the peel sees the carrier), while a node // typed by its declaring module's alias spelling String is re-resolved by name in the caller's @@ -5818,31 +5832,44 @@ data direct_call_shape_wall_note: String = "WALL (DESIGN §5) — the compile se // of the kernel spelling String may find a corpus declaration, which kernel precedence says it never // binds to. fn argument_text_representation(value: Node, type_env: TypeEnv, module_name: String) -> TextRepresentation { - let raw = expression_value_type(e: value) - let peeled = peel_nominal_alias_identity(n: raw, env: type_env, module_name: module_name) - let raw_rep = text_representation_of_type(n: raw, source_indices: type_env.source_indices) - let peeled_rep = text_representation_of_type(n: peeled, source_indices: type_env.source_indices) - let raw_is_kernel_mint = match type_reference_provenance(n: raw) { - KernelMinted { minted_name: _ } => true - CorpusDeclared { decl_file: _ } => false - DeclarationIdentityAbsent => false - } - match raw_rep { - CodePointSequence => CodePointSequence - HostText => if raw_is_kernel_mint { HostText } else { match peeled_rep { CodePointSequence => CodePointSequence HostText => HostText NotText => HostText } } - NotText => peeled_rep - } + text_representation_by_identity(n: expression_value_type(e: value), env: type_env) } fn text_representation_is_text(r: TextRepresentation) -> Bool { - match r { NotText => false HostText => true CodePointSequence => true } + match r { NotText => false HostText => true CodePointSequence => true TextRepresentationUnidentified => false } +} + +// THE UNJUDGED POPULATION IS COUNTED, NOT SILENT (gunbc.rung_drop text_boundary_identity_wall). Where +// one side of a text boundary is identified text and the other has no readable declaration identity, +// the text wall makes no verdict and base compatibility decides; this advisory records each such site, +// located, so the whole-corpus compile's advisory census is the instrument that bounds the drop. +fn text_unjudged_advisories(a: TextRepresentation, b: TextRepresentation, position: String, span: SourceSpan, module_name: String) -> List { + let a_text = text_representation_is_text(r: a) + let b_text = text_representation_is_text(r: b) + let a_unid = match a { TextRepresentationUnidentified => true _ => false } + let b_unid = match b { TextRepresentationUnidentified => true _ => false } + if (a_text && b_unid) || (b_text && a_unid) { + [make_error_node(diagnostic: TextRepresentationUnidentifiedAtBoundary { position: position, span: span }, module_name: module_name)] + } else { [] } +} + +// Two code-point sequences agree by identity, and base's name-keyed arms would compare an alias +// spelling (String) against the structure (FreeMonoid) and misfire, so they are skipped. +// Two HOST texts are NOT short-circuited: base's own checks still judge them (a kernel String at a +// NonEmptyStr refinement is base's refusal, and it stays one). +fn text_representations_both_code_point(a: TextRepresentation, b: TextRepresentation) -> Bool { + match a { + CodePointSequence => match b { CodePointSequence => true _ => false } + _ => false + } } fn text_representations_disagree(a: TextRepresentation, b: TextRepresentation) -> Bool { match a { NotText => false - HostText => match b { CodePointSequence => true HostText => false NotText => false } - CodePointSequence => match b { HostText => true CodePointSequence => false NotText => false } + HostText => match b { CodePointSequence => true TextRepresentationUnidentified => false HostText => false NotText => false } + CodePointSequence => match b { HostText => true TextRepresentationUnidentified => false CodePointSequence => false NotText => false } + TextRepresentationUnidentified => false } } @@ -5869,11 +5896,12 @@ fn direct_call_arg_mismatch_diags( } else { let actual_raw = expression_value_type(e: actual_expr) let actual = peel_nominal_alias_identity(n: actual_raw, env: type_env, module_name: module_name) - let formal_text = if formal_code_point_view(formal: app.formal, source_indices: source_indices) != none { CodePointSequence } else { text_representation_of_type(n: formal, source_indices: source_indices) } + let formal_text = if formal_code_point_view(formal: app.formal, env: type_env) != none { CodePointSequence } else { text_representation_by_identity(n: formal, env: type_env) } let actual_text = argument_text_representation(value: actual_expr, type_env: type_env, module_name: module_name) - if text_representations_disagree(a: formal_text, b: actual_text) { + let unjudged = text_unjudged_advisories(a: formal_text, b: actual_text, position: "a call argument", span: actual_expr.span, module_name: module_name) + concat(unjudged, if text_representations_disagree(a: formal_text, b: actual_text) { [make_error_node(diagnostic: InternalError { message: concat("text representation crossing at a call argument: declared ", node_type_shape(n: formal, source_indices: source_indices), ", produced ", node_type_shape(n: actual, source_indices: source_indices), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text"), span: actual_expr.span }, module_name: module_name)] - } else if text_representation_is_text(r: formal_text) && text_representation_is_text(r: actual_text) { + } else if text_representations_both_code_point(a: formal_text, b: actual_text) { [] } else if direct_call_arg_type_mismatch( formal: formal, substitution_basis: app.formal.substitution_basis, actual: actual, actual_expr: actual_expr, type_env: type_env, module_name: module_name, source_indices: source_indices) { @@ -5885,7 +5913,7 @@ fn direct_call_arg_mismatch_diags( )] } else { [] - } + }) } Absent => [] } @@ -5930,7 +5958,7 @@ type Tier2bBt { // could call string_length / char_at / substring on its own values with zero diagnostics while // rustc refused the emission. Each argument bound to a host-text parameter // (v1.compiler.infer_method builtin_host_text_param_names) is asked the one text-representation -// authority (v1.compiler.coercion text_representation_of_type); a CodePointSequence there refuses, +// authority (v1.compiler.infer_env text_representation_by_identity); a CodePointSequence there refuses, // located at the argument. An argument is matched to its parameter by label, else by position. fn builtin_text_argument_crossing_diags(func_name: String, typed_args: List, scope: InferScope) -> List { let host_params = builtin_host_text_param_names(name: func_name) @@ -5955,6 +5983,7 @@ fn builtin_text_argument_crossing_diags(func_name: String, typed_args: List [] NotText => [] + TextRepresentationUnidentified => [make_error_node(diagnostic: TextRepresentationUnidentifiedAtBoundary { position: concat("argument '", concat(bound_name, concat("' of ", func_name))), span: value.span }, module_name: scope.module_name)] } _ => [] } @@ -6078,7 +6107,7 @@ fn infer_tier2b_builtin_with_kernel_diags(func_name: String, typed_args: List match operand_elem { Present { value: oe } => - set_element_types_mismatch(recv_elem: re, operand_elem: oe, source_indices: scope.type_env.source_indices) + set_element_types_mismatch(recv_elem: re, operand_elem: oe, source_indices: scope.type_env.source_indices, env: Present { value: scope.type_env }) Absent => false } Absent => false @@ -6926,6 +6955,7 @@ fn host_text_unfolded_at_code_point_boundary(value: Node, destination_type: Node } else { value } CodePointSequence => value NotText => value + TextRepresentationUnidentified => value } _ => value } @@ -6941,19 +6971,19 @@ fn host_text_unfolded_at_code_point_boundary(value: Node, destination_type: Node // { String } is the kernel String (kernel precedence; test.claim.self_host_structural_text_witness_test // w_bare_string_with_text_import_stays_host_deterministically), while the by-name peel in that module // finds the imported structural alias. Absent means neither view is a code-point sequence. -fn formal_code_point_view(formal: ResolvedFormal, source_indices: Map) -> Node? { - match text_representation_of_type(n: formal.declared_type, source_indices: source_indices) { +fn formal_code_point_view(formal: ResolvedFormal, env: TypeEnv) -> Node? { + match text_representation_by_identity(n: formal.declared_type, env: env) { HostText => none - _ => formal_code_point_view_peeled(formal: formal, source_indices: source_indices) + _ => formal_code_point_view_peeled(formal: formal, env: env) } } -fn formal_code_point_view_peeled(formal: ResolvedFormal, source_indices: Map) -> Node? { - match text_representation_of_type(n: formal.declaration_bound_conformance, source_indices: source_indices) { +fn formal_code_point_view_peeled(formal: ResolvedFormal, env: TypeEnv) -> Node? { + match text_representation_by_identity(n: formal.declaration_bound_conformance, env: env) { CodePointSequence => Present { value: formal.declaration_bound_conformance } - _ => match text_representation_of_type(n: formal.substitution_basis, source_indices: source_indices) { + _ => match text_representation_by_identity(n: formal.substitution_basis, env: env) { CodePointSequence => Present { value: formal.substitution_basis } - _ => match text_representation_of_type(n: formal.declared_type, source_indices: source_indices) { + _ => match text_representation_by_identity(n: formal.declared_type, env: env) { CodePointSequence => Present { value: formal.declared_type } _ => none } @@ -6987,7 +7017,7 @@ fn infer_call_arguments_generic_pass(call_args: List, value_params: List match formals |> skip(formal_index) |> first { - Present { value: carried } => formal_code_point_view(formal: carried, source_indices: scope.type_env.source_indices) + Present { value: carried } => formal_code_point_view(formal: carried, env: scope.type_env) Absent => none } CallArgumentFormalUnavailable => none @@ -7209,9 +7239,9 @@ type LiteralBoundary | LiteralBoundaryElaborated { elaboration: LiteralElaboration, destination_type: Node } | LiteralBoundaryRefused { message: String } -// A CODE-POINT-SEQUENCE DESTINATION IS KEYED BY ITS STRUCTURE. v1.compiler.coercion -// text_representation_of_type establishes that the destination is a corpus-declared FreeMonoid over -// Char however it is spelled -- the qualified v2.std.text.String peels to std.algebra FreeMonoid, but +// A CODE-POINT-SEQUENCE DESTINATION IS KEYED BY ITS STRUCTURE. v1.compiler.infer_env +// text_representation_by_identity establishes by declaration identity that the destination is +// std.algebra FreeMonoid over std.types Char however it is spelled -- the qualified v2.std.text.String peels to std.algebra FreeMonoid, but // the same declaration read in its own module (the declaration-bound formal a foreign call site now // uses) keeps its alias name String, and its declaration reference names the alias rather than // the structure. The literal homomorphism row is keyed on the structure that survives resolution @@ -7228,10 +7258,11 @@ fn literal_boundary_elaboration(lit: LiteralValue, expected: Node?, scope: Infer if exp.return_cardinality == CardOptional { LiteralBoundaryPlain } else { - let is_code_point_sequence = match text_representation_of_type(n: exp, source_indices: scope.type_env.source_indices) { + let is_code_point_sequence = match text_representation_by_identity(n: exp, env: scope.type_env) { CodePointSequence => true HostText => false NotText => false + TextRepresentationUnidentified => false } let identified = if is_code_point_sequence { Present { value: code_point_sequence_structure_ref() } @@ -8566,7 +8597,8 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu let branch_diags = if node_type_compatible( left: then_rt, right: else_rt, - source_indices: scope.type_env.source_indices + source_indices: scope.type_env.source_indices, + env: Present { value: scope.type_env } ) { [] } else { @@ -8995,7 +9027,8 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu index_type: index_type, span: span, module_name: scope.module_name, - source_indices: scope.type_env.source_indices + source_indices: scope.type_env.source_indices, + env: Present { value: scope.type_env } ) } _ => none } @@ -9045,7 +9078,8 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu end_type: end_type, span: span, module_name: scope.module_name, - source_indices: scope.type_env.source_indices + source_indices: scope.type_env.source_indices, + env: Present { value: scope.type_env } ) } _ => none } @@ -9867,7 +9901,7 @@ fn infer_record_lit_structural(occurrence_identity: NodeOccurrenceIdentity, type if direct_call_formal_has_unbound_type_variable(n: field_conformance_type) { [] } else if node_type_compatible( - left: expected_node, right: got_node, source_indices: scope.type_env.source_indices) { + left: expected_node, right: got_node, source_indices: scope.type_env.source_indices, env: Present { value: scope.type_env }) { [] } else if declared_alias_target_matches_produced( expected: expected_node, produced: got_node, env: scope.type_env) { diff --git a/src/v1/04_types.dag b/src/v1/04_types.dag index c53b3e9f066..336632f968b 100644 --- a/src/v1/04_types.dag +++ b/src/v1/04_types.dag @@ -2,7 +2,7 @@ module v1.compiler.infer_types import std.occurrence_identity { OccurrenceSynthetic } -import v1.compiler.infer_env { TypeBinding, TypeEnv, type_reference_declaration_ref } +import v1.compiler.infer_env { TypeBinding, TypeEnv, type_reference_declaration_ref, text_crossing_by_identity } import std.types { SourceSpan, is_container_type, container_expected_arity, container_param_name, container_template_algebra, container_template_alias_algebra, container_template_alias_rows } @@ -20,7 +20,6 @@ import std.algebra { import std.syntax { AlgAdd, AlgebraFieldKind, BinOp, LitStr, LiteralValue } -import v1.compiler.coercion { text_representations_cross } import std.coercion { SubtractionRefinement, SubtractionStaysInOperandAlgebra, SubtractionEscapesTo, SubtractionRefinementAmbiguous, dag_subtraction_refinement } @@ -923,7 +922,19 @@ fn node_type_shape(n: Node, source_indices: Map) -> String } } -fn node_type_compatible(left: Node, right: Node, source_indices: Map) -> Bool { +// The text judgment asks the corpus declaration index, which lives on the TypeEnv. Every checker +// call site passes its env. The one caller that holds none -- v1.compiler.emit_info's variant +// field-summary builder, which runs without a census and asks whether a shared field has one type, +// not whether a value crosses a representation boundary -- passes none, and the judgment is not +// asked there (declared on gunbc.rung_drop text_boundary_identity_wall). +fn text_crossing_in_optional_env(left: Node, right: Node, env: TypeEnv?) -> Bool { + match env { + Present { value: e } => text_crossing_by_identity(left: left, right: right, env: e) + Absent => false + } +} + +fn node_type_compatible(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { let left_err = if left.inferred != none { is_compiler_error(inferred: left.inferred.value) } else { false } let right_err = if right.inferred != none { is_compiler_error(inferred: right.inferred.value) } else { false } let left_tv = if left.inferred != none { is_type_variable(inferred: left.inferred.value) } else { false } @@ -934,11 +945,11 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map node_type_compatible(left: a, right: b, source_indices: source_indices)) + callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_compatible(left: a, right: b, source_indices: source_indices, env: env)) } else { let left_is_container = node_is_element_collection(n: left, source_indices: source_indices) @@ -955,7 +966,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map true } Absent => true @@ -975,7 +986,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map true } Absent => true @@ -987,7 +998,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map) -> Bool { +fn node_type_equals(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { let left_err = if left.inferred != none { is_compiler_error(inferred: left.inferred.value) } else { false } let right_err = if right.inferred != none { is_compiler_error(inferred: right.inferred.value) } else { false } let left_tv = if left.inferred != none { is_type_variable(inferred: left.inferred.value) } else { false } @@ -1043,18 +1054,20 @@ fn node_type_equals(left: Node, right: Node, source_indices: Map is a code-point sequence; v1.compiler.coercion text_representations_cross asks -// the one authority the Rust renderer also reads, so node_type_equals_core refuses the pairing -// before any leaf-name arm can match `String` against `String`. A kernel LITERAL never reaches this +// TEXT REPRESENTATIONS ARE COMPARED BY THE DECLARATION THEY RESOLVE TO, NOT BY SPELLING (XL-0T +// ruling B; gunbc.rung_drop text_boundary_identity_wall; review 72252). Kernel String is host text +// and std.algebra FreeMonoid over std.types Char is a code-point sequence; v1.compiler.infer_env +// text_crossing_by_identity reads each side's declaration by its declaring span or qualified name, +// never by re-resolving the spelling in this comparison's scope, and refuses the pairing (or a text +// side against an unidentified one) before any leaf-name arm can match `String` against `String`. +// That is why these relations carry the TypeEnv: the corpus-wide declaration index lives there. A kernel LITERAL never reaches this // point at a code-point-sequence boundary -- v1.compiler.infer literal_boundary_elaboration has // already replaced it with its unfold image, typed as the destination -- so what this refuses is // exactly the NON-literal crossing, which has no declared route. The wall was once landed in these @@ -1093,12 +1106,12 @@ fn callable_signatures_agree(left: Node, right: Node, agree: fn(Node, Node) -> B } } -fn node_type_equals_core(left: Node, right: Node, source_indices: Map) -> Bool { - if text_representations_cross(left: left, right: right, source_indices: source_indices) { +fn node_type_equals_core(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { + if text_crossing_in_optional_env(left: left, right: right, env: env) { false } else if left.connective == Arrow || (right.connective == Arrow) { left.connective == right.connective - && callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices)) + && callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, env: env)) } else { let left_leaf = left.connective == NoConnective && left.children |> count == 0 && left.properties |> count == 0 let right_leaf = right.connective == NoConnective && right.children |> count == 0 && right.properties |> count == 0 @@ -1116,7 +1129,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map enumerate |> all(pair => match right.children |> skip(pair.first) |> first { - Present { value: right_child } => node_type_equals(left: pair.second, right: right_child, source_indices: source_indices) + Present { value: right_child } => node_type_equals(left: pair.second, right: right_child, source_indices: source_indices, env: env) Absent => false } ) @@ -1137,7 +1150,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map first { Present { value: left_ch } => match right.children |> first { - Present { value: right_ch } => node_type_equals(left: child_type_node(ch: left_ch), right: child_type_node(ch: right_ch), source_indices: source_indices) + Present { value: right_ch } => node_type_equals(left: child_type_node(ch: left_ch), right: child_type_node(ch: right_ch), source_indices: source_indices, env: env) Absent => false } Absent => false @@ -1156,7 +1169,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map match right.children |> skip(1) |> first { Present { value: right_second } => - node_type_equals(left: child_type_node(ch: left_first), right: child_type_node(ch: right_first), source_indices: source_indices) && node_type_equals(left: child_type_node(ch: left_second), right: child_type_node(ch: right_second), source_indices: source_indices) + node_type_equals(left: child_type_node(ch: left_first), right: child_type_node(ch: right_first), source_indices: source_indices, env: env) && node_type_equals(left: child_type_node(ch: left_second), right: child_type_node(ch: right_second), source_indices: source_indices, env: env) Absent => false } Absent => false @@ -1170,7 +1183,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map count > 0 && right.params |> count > 0 { - callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices)) + callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, env: env)) } else { false diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 6a3ebbf5715..63d517b370e 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -148,11 +148,11 @@ import v1.compiler.coercion { coerce_primitive_type, is_copy, target_callable, rust_lookup_exact_binding, declaration_realizes_natively_on_rust, type_reference_realization, declaration_realization, type_realization_decision, realized_checkpoint, realization_host_numeric_spelling, - realization_is_host_numeric, provenance_declares_structurally, text_representation_of_type + realization_is_host_numeric, provenance_declares_structurally } import std.coercion { TypeDeclarationProvenance, CorpusDeclared, KernelMinted, DeclarationIdentityAbsent, - TypeRealizationDecision, HostText, CodePointSequence, NotText + TypeRealizationDecision } import v1.compiler.dag_collect_support { connective_name } import v1.compiler.trait_derive_emit { @@ -901,15 +901,30 @@ fn rust_seed_host_container_base(name: String) -> String? { } } +fn rust_host_text_carrier_elem_name(n: Node, source_indices: Map) -> String { + match n.children |> first { + Present { value: ch } => authored_name_at(source_indices: source_indices, node: ch) + Absent => + match find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) { + Present { value: applied } => + match applied.children |> first { + Present { value: ach } => authored_name_at(source_indices: source_indices, node: ach) + Absent => "" + } + Absent => "" + } + } +} // An arrow is never a text carrier, whatever its authored name resolves to: `fn(String) -> String` // answered true here (its name reads as its return leaf) ahead of every Arrow arm, so the type // argument rendered `String` (E0277 x2 at v2.std.compilers.target_model). // -// THE ANSWER IS v1.compiler.coercion text_representation_of_type, the one authority the type -// checker's compatibility relation also reads, so a pairing the checker admits is one this renderer -// realizes with one carrier (gunbc.rung_drop text_boundary_identity_wall). The history below is why -// that authority keys as it does. +// NOT YET THE CHECKER'S AUTHORITY, AND DECLARED AS SUCH (gunbc.rung_drop text_boundary_identity_wall): +// the checker decides text representation by declaration identity (v1.compiler.infer_env +// text_representation_by_identity), which needs the TypeEnv census; these renderers hold no env, so +// this arm keeps its provenance-keyed answer, measured to render the self-host crate byte-identically +// to the pre-change emitter. The trigger that unifies them is the render paths carrying the env. // // The String arm is keyed on the resolved declaration's PROVENANCE, never on the spelling alone // (DESIGN section 3, and gunbc.recurring_failure_mode @@ -939,10 +954,21 @@ fn rust_seed_host_container_base(name: String) -> String? { // before this gate, Agrees after). fn is_host_text_carrier_type(n: Node, source_indices: Map) -> Bool { if n.connective == Arrow { return false } - match text_representation_of_type(n: n, source_indices: source_indices) { - HostText => true - CodePointSequence => false - NotText => false + let nm = authored_name_at(source_indices: source_indices, node: n) + if nm == "String" { + !provenance_declares_structurally(dag_name: "String", p: type_reference_provenance(n: n)) + } else if nm == "FreeMonoid" || nm == "List" { + if rust_host_text_carrier_elem_name(n: n, source_indices: source_indices) != "Char" { + false + } else { + match type_reference_provenance(n: n) { + CorpusDeclared { decl_file: _ } => false + KernelMinted { minted_name: _ } => true + DeclarationIdentityAbsent => true + } + } + } else { + false } } diff --git a/src/v1/coercion.dag b/src/v1/coercion.dag index a495cee1eac..7b6f864042f 100644 --- a/src/v1/coercion.dag +++ b/src/v1/coercion.dag @@ -2,7 +2,7 @@ module v1.compiler.coercion import v1.compiler.artifact { RenderTarget, Rust, Python, Go, Dag } -import v1.std.core { qualified_last_segment, Node, Resolved, declaration_provenance_of, type_reference_provenance, NewlineIndex, authored_name_at, find_property } +import v1.std.core { qualified_last_segment, Node, Resolved, declaration_provenance_of, type_reference_provenance } import std.coercion { TypeCheckpoint, InhabitantDecl, CallableRepr, CastSyntax, TypeRealizationDecision, @@ -10,8 +10,7 @@ import std.coercion { ExactSourceIdentityAbsent, BareRowMigratedToExactBinding, BoundRepresentationHasNoRealizationRow, ExactBindingAmbiguousForOneDeclaration, RealizationGround, GroundedByCheckpointRow, GroundedByHostNumericAlias, GroundedByUnidentifiedBareRow, TypeDeclarationProvenance, - CorpusDeclared, KernelMinted, DeclarationIdentityAbsent, - TextRepresentation, HostText, CodePointSequence, NotText + CorpusDeclared, KernelMinted, DeclarationIdentityAbsent } import std.decl_ref { DeclarationRef } @@ -183,97 +182,6 @@ fn provenance_declares_structurally(dag_name: String, p: TypeDeclarationProvenan } } -// WHICH TEXT REPRESENTATION A TYPE NODE DENOTES (std.coercion TextRepresentation). ONE authority, -// read by the type-compatibility relation (v1.compiler.infer_types node_type_equals_core) and by the -// Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type), so the checker cannot admit a -// pairing the emitted program then refuses. -// -// Keyed on the resolved declaration's provenance and its element, never on the spelling alone. A -// corpus-declared text carrier over Char is the code-point sequence WHATEVER it is spelled: a -// resolved `String` reference (a local `type String = FreeMonoid` keeps the alias name -// and carries the Char element), a peeled `FreeMonoid` -- read from the node's resolved name as -// well as its authored spelling, since a local `type Text = FreeMonoid` resolves to a -// FreeMonoid node that keeps the authored spelling Text -- or a List. The -// spelling String with no element is structural only where its declaration is on the structural -// roster (structural_declaration_modules_for); an undotted kernel mint, or a reference whose -// declaration is absent, is host text -- the same answer the renderer has always given those nodes. -fn text_carrier_element_name(n: Node, source_indices: Map) -> String { - match n.children |> first { - Present { value: ch } => authored_name_at(source_indices: source_indices, node: ch) - Absent => - match find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) { - Present { value: applied } => - match applied.children |> first { - Present { value: ach } => authored_name_at(source_indices: source_indices, node: ach) - Absent => "" - } - Absent => "" - } - } -} - -// A dotted kernel mint is not the kernel: the resolver mints a stand-in on the reference span for a -// QUALIFIED structural reference (v2.std.text.String arrives as a kernel-minted -// std.algebra.FreeMonoid), so it names the corpus declaration exactly as CorpusDeclared does. -// Only an undotted mint -- the kernel's own String, or a bare List the seed mints -- is host. -fn provenance_is_corpus_declared(p: TypeDeclarationProvenance) -> Bool { - match p { - CorpusDeclared { decl_file: _ } => true - KernelMinted { minted_name: m } => contains(m, ".") - DeclarationIdentityAbsent => false - } -} - -// A QUALIFIED `...String` SPELLING, written directly or reached through a local alias (`type -// LocalText = v2.std.text.String`, which resolves to a -// childless leaf that keeps its authored spelling and carries the qualified target only as its -// name) names its target module. That target is a code-point sequence exactly when its module is on the structural roster. -// The roster is keyed by file path, so the module path is mapped onto that path here -- the same -// positional residue structural_declaration_modules_for already declares, not a second authority. -fn qualified_string_names_structural_declaration(qualified: String) -> Bool { - let module_path = substring(s: qualified, start: 0, end: string_length(s: qualified) - string_length(s: ".String")) - let module_file = concat(replace(module_path, ".", "/"), ".dag") - structural_declaration_modules_for(dag_name: "String") |> any(f => contains(f, module_file)) -} - -fn text_representation_of_type(n: Node, source_indices: Map) -> TextRepresentation { - let authored_full = authored_name_at(source_indices: source_indices, node: n) - let authored = qualified_last_segment(name: authored_full) - let resolved_name = qualified_last_segment(name: n.name) - let element = qualified_last_segment(name: text_carrier_element_name(n: n, source_indices: source_indices)) - let p = type_reference_provenance(n: n) - let is_monoid = authored == "FreeMonoid" || authored == "List" || resolved_name == "FreeMonoid" || resolved_name == "List" - if is_monoid { - if element != "Char" { NotText } - else if provenance_is_corpus_declared(p: p) { CodePointSequence } - else { HostText } - } else if resolved_name == "String" && authored != "String" && contains(n.name, ".") { - if qualified_string_names_structural_declaration(qualified: n.name) { CodePointSequence } else { NotText } - } else if authored == "String" && contains(authored_full, ".") { - if qualified_string_names_structural_declaration(qualified: authored_full) { CodePointSequence } else { NotText } - } else if authored == "String" { - if element == "Char" && provenance_is_corpus_declared(p: p) { CodePointSequence } - else if element != "" { NotText } - else if provenance_declares_structurally(dag_name: "String", p: p) { CodePointSequence } - else { HostText } - } else { - NotText - } -} - -// A CROSSING between the two representations: both sides are text and they disagree. Every such -// pairing is refused by the compatibility relation; the literal half never reaches it, because a -// kernel literal at a code-point-sequence boundary is elaborated through the unfold first. -fn text_representations_cross(left: Node, right: Node, source_indices: Map) -> Bool { - let l = text_representation_of_type(n: left, source_indices: source_indices) - let r = text_representation_of_type(n: right, source_indices: source_indices) - match l { - NotText => false - HostText => match r { CodePointSequence => true HostText => false NotText => false } - CodePointSequence => match r { HostText => true CodePointSequence => false NotText => false } - } -} - data numeric_realization_identity_note: String = "Realization is keyed on the DECLARING MODULE of the type, never on the authored spelling alone. Two declarations may share a spelling -- std.nat.Nat is CommutativeSemiring and realizes natively, while v2.std.nat.Nat is the Peano coproduct Zero|Succ and must NOT -- so a bare-name rule realizes the wrong one. decl_file is the resolved declaration's ident_span file; the empty string means identity is UNKNOWN at this site, which yields NO realization (render structurally) rather than a guess. This replaces the deleted rust_corpus_repr closure-provenance switch: what a declaration realizes as is a fact about that declaration and its target, never about which other sources happen to share the closure. RELOCATED here from v1.compiler.emit_rust (smart-ram-730, adhoc-2ea6fb98-a3f, 2026-08-21, review 54335) alongside numeric_realization_roster_extension_note, numeric_realization_declaring_modules, decl_file_realizes_natively and rust_seed_host_numeric_alias -- completing the relocation structural_declaration_modules_for's own precedent already established for the negative-form (structural) half of this two-authority shape; emit_rust now imports rust_seed_host_numeric_alias back from here exactly as it already imports lookup_checkpoint, one direction, no cycle. See v1.compiler.emit_rust numeric_realization_relocation_note for the fuller account." data numeric_realization_roster_extension_note: String = "THIS ROSTER IS A LIVE ENUMERATION AND A ROW MAY ONLY BE ADDED FOR A DECLARATION WHOSE NATIVE REALIZATION IS ALREADY TRUE, NEVER TO MAKE A FAILING SITE COMPILE. A module belongs here when the types it declares ARE the host numeric type at the target -- dag/std/nat.dag and dag/std/integer.dag are the two grounded numeric authorities, and the .expect("AccountId binding"); assert!( - !node_type_compatible(user_id.clone(), account_id, result.source_indices.clone()), + !node_type_compatible( + user_id.clone(), + account_id, + result.source_indices.clone(), + Some(module.type_env.clone()) + ), "PD-3: node_type_compatible must reject brand-twin UserId-for-AccountId" ); assert!( - node_type_compatible(user_id.clone(), user_id, result.source_indices.clone()), + node_type_compatible( + user_id.clone(), + user_id, + result.source_indices.clone(), + Some(module.type_env.clone()) + ), "PD-3: node_type_compatible must accept same-brand UserId-for-UserId" ); } @@ -624,6 +634,7 @@ fn list_int_index_returns_optional_element_type() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert_eq!( @@ -644,6 +655,7 @@ fn malformed_map_index_returns_compiler_error_type() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert_eq!(result.diagnostics.len(), 1); @@ -667,6 +679,7 @@ fn invalid_slice_returns_compiler_error_type() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert_eq!(result.diagnostics.len(), 1); @@ -685,6 +698,7 @@ fn valid_list_slice_preserves_list_type() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert!(result.diagnostics.is_empty()); @@ -711,6 +725,7 @@ fn valid_map_index_preserves_optional_value_type() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert!(result.diagnostics.is_empty()); @@ -1844,6 +1859,7 @@ fn map_index_with_correct_key_type_succeeds() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert!( result.diagnostics.is_empty(), @@ -1870,6 +1886,7 @@ fn map_index_with_wrong_key_type_reports_error() { zero_span(), "test".to_string(), empty_source_indices(), + None, ); assert_eq!( result.diagnostics.len(), @@ -1932,7 +1949,7 @@ fn list_and_freemonoid_compatible_same_element() { let list_sym = container_node("List".to_string(), leaf_node("Symbol".to_string())); let fm_sym = container_node("FreeMonoid".to_string(), leaf_node("Symbol".to_string())); assert!( - node_type_compatible(list_sym, fm_sym, empty_source_indices()), + node_type_compatible(list_sym, fm_sym, empty_source_indices(), None), "List and FreeMonoid are declared aliases — must be compatible at type-comparison" ); } @@ -1941,7 +1958,7 @@ fn list_and_freemonoid_incompatible_different_element() { let list_int = container_node("List".to_string(), leaf_node("Int".to_string())); let fm_string = container_node("FreeMonoid".to_string(), leaf_node("String".to_string())); assert!( - !node_type_compatible(list_int, fm_string, empty_source_indices()), + !node_type_compatible(list_int, fm_string, empty_source_indices(), None), "List vs FreeMonoid differ in element type — must stay incompatible" ); } @@ -1950,7 +1967,7 @@ fn list_freemonoid_compat_is_symmetric() { let fm_sym = container_node("FreeMonoid".to_string(), leaf_node("Symbol".to_string())); let list_sym = container_node("List".to_string(), leaf_node("Symbol".to_string())); assert!( - node_type_compatible(fm_sym, list_sym, empty_source_indices()), + node_type_compatible(fm_sym, list_sym, empty_source_indices(), None), "alias compatibility must hold in both argument orders" ); } diff --git a/src/v1/stage0/src/cli_run/compile_clean.rs b/src/v1/stage0/src/cli_run/compile_clean.rs index 203b3323c24..27329b91c20 100644 --- a/src/v1/stage0/src/cli_run/compile_clean.rs +++ b/src/v1/stage0/src/cli_run/compile_clean.rs @@ -1050,6 +1050,7 @@ pub fn compile_clean_diagnostic_class_specimen() -> Vec { MethodExistenceUndecided { method: s(), receiver_type: s(), span: no_span() }, MethodExistenceFrontierAdmitted { method: s(), receiver_type: s(), trigger: s(), span: no_span() }, ReceiverTypeUnestablished { method: s(), span: no_span() }, + TextRepresentationUnidentifiedAtBoundary { position: s(), span: no_span() }, AlgebraApplicationEvidenceUnavailable { receiver_type: s(), argument_index: 0, span: no_span() }, SiblingOperandEffectOrderUndetermined { construct: s(), first_operand: s(), second_operand: s(), span: no_span() }, PresentArmScrutineeTypeUnresolved { pattern: s(), span: no_span() }, @@ -1538,6 +1539,9 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::MethodNotFound { .. } => "MethodNotFound", CompilerDiagnostic::MethodExistenceUndecided { .. } => "MethodExistenceUndecided", CompilerDiagnostic::ReceiverTypeUnestablished { .. } => "ReceiverTypeUnestablished", + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { .. } => { + "TextRepresentationUnidentifiedAtBoundary" + } CompilerDiagnostic::FrontierOccurrenceBudgetExceeded { .. } => { "FrontierOccurrenceBudgetExceeded" } @@ -1644,6 +1648,9 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::MethodExistenceUndecided { method, .. } => method.clone(), CompilerDiagnostic::MethodExistenceFrontierAdmitted { method, .. } => method.clone(), CompilerDiagnostic::ReceiverTypeUnestablished { method, .. } => method.clone(), + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { position, .. } => { + position.clone() + } CompilerDiagnostic::FrontierOccurrenceBudgetExceeded { method, .. } => method.clone(), CompilerDiagnostic::TestCodeReferenced { referrer, .. } => referrer.clone(), CompilerDiagnostic::TestCodeReferenceAdmitted { referrer, .. } => referrer.clone(), diff --git a/src/v1/stage0/src/std_coercion.rs b/src/v1/stage0/src/std_coercion.rs index 4e59f020ada..f651608dc24 100644 --- a/src/v1/stage0/src/std_coercion.rs +++ b/src/v1/stage0/src/std_coercion.rs @@ -43,6 +43,7 @@ pub enum TextRepresentation { HostText, CodePointSequence, NotText, + TextRepresentationUnidentified, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -293,6 +294,8 @@ pub struct CodePointSequence; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct NotText; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct TextRepresentationUnidentified; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct NoResolutionBoundAtReference; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResolvedNodeIsNotADeclaration; diff --git a/src/v1/stage0/src/v1_compiler_coercion.rs b/src/v1/stage0/src/v1_compiler_coercion.rs index c401ff8af88..d7c3bb2202f 100644 --- a/src/v1/stage0/src/v1_compiler_coercion.rs +++ b/src/v1/stage0/src/v1_compiler_coercion.rs @@ -28,14 +28,13 @@ use crate::std_coercion::RealizationRefusalCause::{ DeclarationIdentityUnavailable, ExactBindingAmbiguousForOneDeclaration, ExactSourceIdentityAbsent, }; -use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; use crate::std_coercion::TypeRealizationDecision::{RealizationRefused, Realized, Unrealized}; pub use crate::std_coercion::{ CallableRepr, CastSyntax, InhabitantDecl, RealizationGround, RealizationRefusalCause, - TextRepresentation, TypeCheckpoint, TypeDeclarationProvenance, TypeRealizationDecision, + TypeCheckpoint, TypeDeclarationProvenance, TypeRealizationDecision, }; pub use crate::std_decl_ref::DeclarationRef; pub use crate::std_target_representation::ExactBindingResolution; @@ -53,10 +52,9 @@ use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::InferredNode::Resolved; pub use crate::v1_std_core::{ - authored_name_at, declaration_provenance_of, find_property, qualified_last_segment, - type_reference_provenance, + declaration_provenance_of, qualified_last_segment, type_reference_provenance, }; -pub use crate::v1_std_core::{InferredNode, NewlineIndex, Node}; +pub use crate::v1_std_core::{InferredNode, Node}; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; use im::{vector as vec, HashMap, OrdSet as BTreeSet, Vector as Vec}; @@ -204,165 +202,6 @@ pub fn provenance_declares_structurally( } } -pub fn text_carrier_element_name( - n: Rc, - source_indices: Rc>>, -) -> String { - match n.children.clone().first().cloned() { - Some(ch) => crate::v1_std_core::authored_name_at(source_indices.clone(), ch.clone()), - std::option::Option::None => match crate::v1_std_core::find_property( - n.properties.clone(), - "__applied_type_args".to_string(), - source_indices.clone(), - ) { - Some(applied) => match applied.children.clone().first().cloned() { - Some(ach) => { - crate::v1_std_core::authored_name_at(source_indices.clone(), ach.clone()) - } - std::option::Option::None => "".to_string(), - }, - std::option::Option::None => "".to_string(), - }, - } -} - -pub fn provenance_is_corpus_declared(p: Rc) -> bool { - match (*p.clone()).clone() { - TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => true, - TypeDeclarationProvenance::KernelMinted { minted_name: m, .. } => { - v1_rt::contains(m.clone(), ".".to_string()) - } - TypeDeclarationProvenance::DeclarationIdentityAbsent => false, - } -} - -pub fn qualified_string_names_structural_declaration(qualified: String) -> bool { - { - let module_path = v1_rt::substring( - &qualified, - 0, - v1_rt::int_sub( - v1_rt::string_length(&qualified), - v1_rt::string_length(&".String".to_string()), - ), - ); - let module_file = v1_rt::concat( - v1_rt::replace(module_path.clone(), ".".to_string(), "/".to_string()), - ".dag".to_string(), - ); - { - let mut __found = false; - for f in structural_declaration_modules_for("String".to_string()) - .iter() - .cloned() - { - if v1_rt::contains(f.clone(), module_file.clone()) { - __found = true; - break; - } - } - __found - } - } -} - -pub fn text_representation_of_type( - n: Rc, - source_indices: Rc>>, -) -> TextRepresentation { - { - let authored_full = crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()); - let authored = crate::v1_std_core::qualified_last_segment(authored_full.clone()); - let resolved_name = crate::v1_std_core::qualified_last_segment(n.name.clone()); - let element = crate::v1_std_core::qualified_last_segment(text_carrier_element_name( - n.clone(), - source_indices.clone(), - )); - let p = crate::v1_std_core::type_reference_provenance(n.clone()); - let is_monoid = ((((authored.clone() == "FreeMonoid".to_string()) - || (authored.clone() == "List".to_string())) - || (resolved_name.clone() == "FreeMonoid".to_string())) - || (resolved_name.clone() == "List".to_string())); - if is_monoid.clone() { - if (element.clone() != "Char".to_string()) { - TextRepresentation::NotText - } else { - if provenance_is_corpus_declared(p.clone()) { - TextRepresentation::CodePointSequence - } else { - TextRepresentation::HostText - } - } - } else { - if (((resolved_name.clone() == "String".to_string()) - && (authored.clone() != "String".to_string())) - && v1_rt::contains(n.name.clone(), ".".to_string())) - { - if qualified_string_names_structural_declaration(n.name.clone()) { - TextRepresentation::CodePointSequence - } else { - TextRepresentation::NotText - } - } else { - if ((authored.clone() == "String".to_string()) - && v1_rt::contains(authored_full.clone(), ".".to_string())) - { - if qualified_string_names_structural_declaration(authored_full.clone()) { - TextRepresentation::CodePointSequence - } else { - TextRepresentation::NotText - } - } else { - if (authored.clone() == "String".to_string()) { - if ((element.clone() == "Char".to_string()) - && provenance_is_corpus_declared(p.clone())) - { - TextRepresentation::CodePointSequence - } else { - if (element.clone() != "".to_string()) { - TextRepresentation::NotText - } else { - if provenance_declares_structurally("String".to_string(), p.clone()) - { - TextRepresentation::CodePointSequence - } else { - TextRepresentation::HostText - } - } - } - } else { - TextRepresentation::NotText - } - } - } - } - } -} - -pub fn text_representations_cross( - left: Rc, - right: Rc, - source_indices: Rc>>, -) -> bool { - { - let l = text_representation_of_type(left.clone(), source_indices.clone()); - let r = text_representation_of_type(right.clone(), source_indices.clone()); - match l.clone() { - TextRepresentation::NotText => false, - TextRepresentation::HostText => match r.clone() { - TextRepresentation::CodePointSequence => true, - TextRepresentation::HostText => false, - TextRepresentation::NotText => false, - }, - TextRepresentation::CodePointSequence => match r.clone() { - TextRepresentation::HostText => true, - TextRepresentation::CodePointSequence => false, - TextRepresentation::NotText => false, - }, - } - } -} - pub fn numeric_realization_identity_note() -> String { thread_local! { static CACHED: String = { diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 882afafe589..6111c1ee97c 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -68,14 +68,11 @@ pub use crate::gunbc_structural_realization_bindings::{ }; pub use crate::std_algebra::AlgebraFieldTemplate; pub use crate::std_algebra::{is_collection_filter_template, trim}; -use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; use crate::std_coercion::TypeRealizationDecision::*; -pub use crate::std_coercion::{ - TextRepresentation, TypeDeclarationProvenance, TypeRealizationDecision, -}; +pub use crate::std_coercion::{TypeDeclarationProvenance, TypeRealizationDecision}; pub use crate::std_decl_ref::decl_ref; use crate::std_decl_ref::DeclField::WholeDeclaration; pub use crate::std_decl_ref::{DeclField, DeclarationRef}; @@ -135,7 +132,7 @@ pub use crate::v1_compiler_coercion::{ coerce_primitive_type, declaration_realization, declaration_realizes_natively_on_rust, is_copy, provenance_declares_structurally, realization_host_numeric_spelling, realization_is_host_numeric, realized_checkpoint, rust_lookup_exact_binding, target_callable, - text_representation_of_type, type_realization_decision, type_reference_realization, + type_realization_decision, type_reference_realization, }; pub use crate::v1_compiler_compiler_tests_rust::compiler_tests_source; pub use crate::v1_compiler_dag_collect_support::connective_name; @@ -1348,6 +1345,28 @@ pub fn rust_seed_host_container_base(name: String) -> Option { } } +pub fn rust_host_text_carrier_elem_name( + n: Rc, + source_indices: Rc>>, +) -> String { + match n.children.clone().first().cloned() { + Some(ch) => crate::v1_std_core::authored_name_at(source_indices.clone(), ch.clone()), + std::option::Option::None => match crate::v1_std_core::find_property( + n.properties.clone(), + "__applied_type_args".to_string(), + source_indices.clone(), + ) { + Some(applied) => match applied.children.clone().first().cloned() { + Some(ach) => { + crate::v1_std_core::authored_name_at(source_indices.clone(), ach.clone()) + } + std::option::Option::None => "".to_string(), + }, + std::option::Option::None => "".to_string(), + }, + } +} + pub fn is_host_text_carrier_type( n: Rc, source_indices: Rc>>, @@ -1356,13 +1375,28 @@ pub fn is_host_text_carrier_type( if (n.connective.clone() == Connective::Arrow) { return false; } - match crate::v1_compiler_coercion::text_representation_of_type( - n.clone(), - source_indices.clone(), - ) { - TextRepresentation::HostText => true, - TextRepresentation::CodePointSequence => false, - TextRepresentation::NotText => false, + let nm = crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()); + if (nm.clone() == "String".to_string()) { + !crate::v1_compiler_coercion::provenance_declares_structurally( + "String".to_string(), + crate::v1_std_core::type_reference_provenance(n.clone()), + ) + } else { + if ((nm.clone() == "FreeMonoid".to_string()) || (nm.clone() == "List".to_string())) { + if (rust_host_text_carrier_elem_name(n.clone(), source_indices.clone()) + != "Char".to_string()) + { + false + } else { + match (*crate::v1_std_core::type_reference_provenance(n.clone())).clone() { + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, + TypeDeclarationProvenance::DeclarationIdentityAbsent => true, + } + } + } else { + false + } } } } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index ada31c4f0c3..70a634299bd 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -19,7 +19,9 @@ pub use crate::std_algebra::carrier_container_equality_rows; use crate::std_algebra::CollectionSizeEffect::ShrinkEffect; pub use crate::std_algebra::{CollectionSizeEffect, FreeMonoid}; use crate::std_coercion::SubtractionRefinement::SubtractionRefinementAmbiguous; -use crate::std_coercion::TextRepresentation::{CodePointSequence, HostText, NotText}; +use crate::std_coercion::TextRepresentation::{ + CodePointSequence, HostText, NotText, TextRepresentationUnidentified, +}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; @@ -86,9 +88,7 @@ pub use crate::std_types::{ container_param_name, container_template_algebra, is_kernel_type, kernel_type_set, }; pub use crate::std_types::{NonEmptyStr, SourceSpan}; -pub use crate::v1_compiler_coercion::{ - provenance_realizes_natively, text_representation_of_type, text_representations_cross, -}; +pub use crate::v1_compiler_coercion::provenance_realizes_natively; pub use crate::v1_compiler_infer_access::AccessCheckResultNode; pub use crate::v1_compiler_infer_access::{check_index_access_node, check_slice_access_node}; pub use crate::v1_compiler_infer_cycle::detect_type_cycles_kahn; @@ -119,7 +119,8 @@ pub use crate::v1_compiler_infer_env::{ qualified_all_but_last, qualify_borrowed_inferred, qualify_borrowed_type_names, qualify_decl_reference_positions, str_bindings_from_bindings, symbol_index_insert, symbol_index_insert_decl, symbol_index_insert_service, symbol_index_lookup, - type_reference_declaration, type_reference_declaration_ref, unit_variant_index_shadow_insert, + text_crossing_by_identity, text_representation_by_identity, type_reference_declaration, + type_reference_declaration_ref, unit_variant_index_shadow_insert, }; pub use crate::v1_compiler_infer_env::{ GlobalBareCandidate, GlobalBareLookupState, GuardedTypeEnvCacheMerge, ServiceCensusEntry, @@ -254,9 +255,9 @@ use crate::v1_std_core::CompilerDiagnostic::{ FrontierOccurrenceBudgetExceeded, InternalError, MethodExistenceFrontierAdmitted, MethodExistenceUndecided, MethodNotFound, MissingField, OptionalCastNotEliminated, ReceiverTypeUnestablished, ServiceConfigReferenceJudgmentDeferred, - SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, TypeArgumentArityMismatch, - TypeMismatch, TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, - VariantCollision, + SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, + TextRepresentationUnidentifiedAtBoundary, TypeArgumentArityMismatch, TypeMismatch, + TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, VariantCollision, }; use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::DeclarationMarker::Unmarked; @@ -1957,15 +1958,44 @@ pub fn declared_type_conformance_diags( produced: Rc, span: Rc, scope: Rc, +) -> Rc>> { + v1_rt::concat( + text_unjudged_advisories( + crate::v1_compiler_infer_env::text_representation_by_identity( + declared.clone(), + scope.type_env.clone(), + ), + crate::v1_compiler_infer_env::text_representation_by_identity( + produced.clone(), + scope.type_env.clone(), + ), + "a declared type".to_string(), + span.clone(), + scope.module_name.clone(), + ), + declared_type_conformance_diags_core( + declared.clone(), + produced.clone(), + span.clone(), + scope.clone(), + ), + ) +} + +pub fn declared_type_conformance_diags_core( + declared: Rc, + produced: Rc, + span: Rc, + scope: Rc, ) -> Rc>> { { let si = scope.type_env.clone().source_indices.clone(); let both_ground = (conformance_ground_type(declared.clone(), si.clone()) && conformance_ground_type(produced.clone(), si.clone())); - if crate::v1_compiler_coercion::text_representations_cross( + if crate::v1_compiler_infer_env::text_crossing_by_identity( declared.clone(), produced.clone(), - si.clone(), + scope.type_env.clone(), ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape(declared.clone(), si.clone()), @@ -2069,6 +2099,7 @@ pub fn declared_type_conformance_diags( declared.clone(), produced.clone(), si.clone(), + Some(scope.type_env.clone()), ) { Rc::new(vec![]) } else { @@ -2644,6 +2675,7 @@ pub fn set_element_types_mismatch( recv_elem: Rc, operand_elem: Rc, source_indices: Rc>>, + env: Option>, ) -> bool { ((set_element_type_is_concrete(recv_elem.clone()) && set_element_type_is_concrete(operand_elem.clone())) @@ -2651,6 +2683,7 @@ pub fn set_element_types_mismatch( recv_elem.clone(), operand_elem.clone(), source_indices.clone(), + env.clone(), )) } @@ -3804,6 +3837,7 @@ pub fn match_arm_types_are_proven_disjoint( unified_arm_type.clone(), arm_type.clone(), source_indices.clone(), + Some(scope.type_env.clone()), ) == false)) } } @@ -7427,6 +7461,7 @@ pub fn callable_component_ground_mismatch( formal_part.clone(), instantiated.clone(), source_indices.clone(), + std::option::Option::None, ) } else { false @@ -7687,10 +7722,10 @@ pub fn direct_call_arg_type_mismatch( { false } else { - (((crate::v1_compiler_coercion::text_representations_cross( + (((crate::v1_compiler_infer_env::text_crossing_by_identity( formal.clone(), actual.clone(), - source_indices.clone(), + type_env.clone(), ) || nominal_call_arg_brand_mismatch( formal.clone(), actual.clone(), @@ -8372,45 +8407,10 @@ pub fn argument_text_representation( type_env: Rc, module_name: String, ) -> TextRepresentation { - { - let raw = expression_value_type(value.clone()); - let peeled = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity( - raw.clone(), - type_env.clone(), - module_name.clone(), - ); - let raw_rep = crate::v1_compiler_coercion::text_representation_of_type( - raw.clone(), - type_env.source_indices.clone(), - ); - let peeled_rep = crate::v1_compiler_coercion::text_representation_of_type( - peeled.clone(), - type_env.source_indices.clone(), - ); - let raw_is_kernel_mint = - match (*crate::v1_std_core::type_reference_provenance(raw.clone())).clone() { - TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, - TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, - TypeDeclarationProvenance::DeclarationIdentityAbsent => false, - }; - match raw_rep.clone() { - TextRepresentation::CodePointSequence => TextRepresentation::CodePointSequence, - TextRepresentation::HostText => { - if raw_is_kernel_mint.clone() { - TextRepresentation::HostText - } else { - match peeled_rep.clone() { - TextRepresentation::CodePointSequence => { - TextRepresentation::CodePointSequence - } - TextRepresentation::HostText => TextRepresentation::HostText, - TextRepresentation::NotText => TextRepresentation::HostText, - } - } - } - TextRepresentation::NotText => peeled_rep.clone(), - } - } + crate::v1_compiler_infer_env::text_representation_by_identity( + expression_value_type(value.clone()), + type_env.clone(), + ) } pub fn text_representation_is_text(r: TextRepresentation) -> bool { @@ -8418,6 +8418,51 @@ pub fn text_representation_is_text(r: TextRepresentation) -> bool { TextRepresentation::NotText => false, TextRepresentation::HostText => true, TextRepresentation::CodePointSequence => true, + TextRepresentation::TextRepresentationUnidentified => false, + } +} + +pub fn text_unjudged_advisories( + a: TextRepresentation, + b: TextRepresentation, + position: String, + span: Rc, + module_name: String, +) -> Rc>> { + { + let a_text = text_representation_is_text(a.clone()); + let b_text = text_representation_is_text(b.clone()); + let a_unid = match a.clone() { + TextRepresentation::TextRepresentationUnidentified => true, + _ => false, + }; + let b_unid = match b.clone() { + TextRepresentation::TextRepresentationUnidentified => true, + _ => false, + }; + if ((a_text.clone() && b_unid.clone()) || (b_text.clone() && a_unid.clone())) { + Rc::new(vec![crate::v1_std_core::make_error_node( + Rc::new( + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: position.clone(), + span: span.clone(), + }, + ), + module_name.clone(), + )]) + } else { + Rc::new(vec![]) + } + } +} + +pub fn text_representations_both_code_point(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::CodePointSequence => true, + _ => false, + }, + _ => false, } } @@ -8426,14 +8471,17 @@ pub fn text_representations_disagree(a: TextRepresentation, b: TextRepresentatio TextRepresentation::NotText => false, TextRepresentation::HostText => match b.clone() { TextRepresentation::CodePointSequence => true, + TextRepresentation::TextRepresentationUnidentified => false, TextRepresentation::HostText => false, TextRepresentation::NotText => false, }, TextRepresentation::CodePointSequence => match b.clone() { TextRepresentation::HostText => true, + TextRepresentation::TextRepresentationUnidentified => false, TextRepresentation::CodePointSequence => false, TextRepresentation::NotText => false, }, + TextRepresentation::TextRepresentationUnidentified => false, } } @@ -8463,19 +8511,20 @@ if match (*actual_expr.expr_data.clone()).clone() { { let actual_raw = expression_value_type(actual_expr.clone()); let actual = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(actual_raw.clone(), type_env.clone(), module_name.clone()); -let formal_text = if (formal_code_point_view(app.formal.clone(), source_indices.clone()) != std::option::Option::None) { +let formal_text = if (formal_code_point_view(app.formal.clone(), type_env.clone()) != std::option::Option::None) { TextRepresentation::CodePointSequence } else { - crate::v1_compiler_coercion::text_representation_of_type(formal.clone(), source_indices.clone()) + crate::v1_compiler_infer_env::text_representation_by_identity(formal.clone(), type_env.clone()) }; let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone(), module_name.clone()); -if text_representations_disagree(formal_text.clone(), actual_text.clone()) { +let unjudged = text_unjudged_advisories(formal_text.clone(), actual_text.clone(), "a call argument".to_string(), actual_expr.span.clone(), module_name.clone()); +v1_rt::concat(unjudged.clone(), if text_representations_disagree(formal_text.clone(), actual_text.clone()) { Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::InternalError { message: v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing at a call argument: declared ".to_string(), crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone())), ", produced ".to_string()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone())), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text".to_string()), span: actual_expr.span.clone(), }), module_name.clone())]) } else { - if (text_representation_is_text(formal_text.clone()) && text_representation_is_text(actual_text.clone())) { + if text_representations_both_code_point(formal_text.clone(), actual_text.clone()) { Rc::new(vec![]) } else { if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { @@ -8484,7 +8533,7 @@ if text_representations_disagree(formal_text.clone(), actual_text.clone()) { Rc::new(vec![]) } } - } + }) } } }, @@ -8552,6 +8601,10 @@ match value.inferred.clone().as_deref().cloned() { TextRepresentation::CodePointSequence => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing: argument '".to_string(), bound_name.clone()), "' of ".to_string()), func_name.clone()), " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence".to_string()), value.span.clone(), scope.module_name.clone())]), TextRepresentation::HostText => Rc::new(vec![]), TextRepresentation::NotText => Rc::new(vec![]), + TextRepresentation::TextRepresentationUnidentified => Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: v1_rt::concat("argument '".to_string(), v1_rt::concat(bound_name.clone(), v1_rt::concat("' of ".to_string(), func_name.clone()))), + span: value.span.clone(), +}), scope.module_name.clone())]), }, _ => Rc::new(vec![]), } @@ -8822,6 +8875,7 @@ pub fn infer_tier2b_builtin_with_kernel_diags( re.clone(), oe.clone(), scope.type_env.clone().source_indices.clone(), + Some(scope.type_env.clone()), ), std::option::Option::None => false, }, @@ -10523,41 +10577,39 @@ pub fn host_text_unfolded_at_code_point_boundary( } TextRepresentation::CodePointSequence => value.clone(), TextRepresentation::NotText => value.clone(), + TextRepresentation::TextRepresentationUnidentified => value.clone(), }, _ => value.clone(), } } -pub fn formal_code_point_view( - formal: Rc, - source_indices: Rc>>, -) -> Option> { - match crate::v1_compiler_coercion::text_representation_of_type( +pub fn formal_code_point_view(formal: Rc, env: Rc) -> Option> { + match crate::v1_compiler_infer_env::text_representation_by_identity( formal.declared_type.clone(), - source_indices.clone(), + env.clone(), ) { TextRepresentation::HostText => std::option::Option::None, - _ => formal_code_point_view_peeled(formal.clone(), source_indices.clone()), + _ => formal_code_point_view_peeled(formal.clone(), env.clone()), } } pub fn formal_code_point_view_peeled( formal: Rc, - source_indices: Rc>>, + env: Rc, ) -> Option> { - match crate::v1_compiler_coercion::text_representation_of_type( + match crate::v1_compiler_infer_env::text_representation_by_identity( formal.declaration_bound_conformance.clone(), - source_indices.clone(), + env.clone(), ) { TextRepresentation::CodePointSequence => Some(formal.declaration_bound_conformance.clone()), - _ => match crate::v1_compiler_coercion::text_representation_of_type( + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( formal.substitution_basis.clone(), - source_indices.clone(), + env.clone(), ) { TextRepresentation::CodePointSequence => Some(formal.substitution_basis.clone()), - _ => match crate::v1_compiler_coercion::text_representation_of_type( + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( formal.declared_type.clone(), - source_indices.clone(), + env.clone(), ) { TextRepresentation::CodePointSequence => Some(formal.declared_type.clone()), _ => std::option::Option::None, @@ -10664,10 +10716,9 @@ pub fn infer_call_arguments_generic_pass( .skip(formal_index.clone() as usize) .next() { - Some(carried) => formal_code_point_view( - carried.clone(), - scope.type_env.clone().source_indices.clone(), - ), + Some(carried) => { + formal_code_point_view(carried.clone(), scope.type_env.clone()) + } std::option::Option::None => std::option::Option::None, }, CallArgumentFormalSelection::CallArgumentFormalUnavailable => { @@ -10992,13 +11043,14 @@ pub fn literal_boundary_elaboration( } else { { let is_code_point_sequence = - match crate::v1_compiler_coercion::text_representation_of_type( + match crate::v1_compiler_infer_env::text_representation_by_identity( exp.clone(), - scope.type_env.clone().source_indices.clone(), + scope.type_env.clone(), ) { TextRepresentation::CodePointSequence => true, TextRepresentation::HostText => false, TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, }; let identified = if is_code_point_sequence.clone() { Some(code_point_sequence_structure_ref()) @@ -13459,6 +13511,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), then_rt.clone(), else_rt.clone(), scope.type_env.clone().source_indices.clone(), + Some(scope.type_env.clone()), ) { Rc::new(vec![]) } else { @@ -14424,6 +14477,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Some(scope.type_env.clone()), )), _ => std::option::Option::None, }, @@ -14491,6 +14545,7 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Some(scope.type_env.clone()), )) } _ => std::option::Option::None, @@ -15933,7 +15988,7 @@ let field_type_diags = match field_declared_type.clone() { if direct_call_formal_has_unbound_type_variable(field_conformance_type.clone()) { Rc::new(vec![]) } else { - if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone()) { + if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone(), Some(scope.type_env.clone())) { Rc::new(vec![]) } else { if declared_alias_target_matches_produced(expected_node.clone(), got_node.clone(), scope.type_env.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_infer_access.rs b/src/v1/stage0/src/v1_compiler_infer_access.rs index 34c008fcdc2..297be8d3ef8 100644 --- a/src/v1/stage0/src/v1_compiler_infer_access.rs +++ b/src/v1/stage0/src/v1_compiler_infer_access.rs @@ -3,6 +3,7 @@ pub use crate::std_types::is_ordered_element_collection; pub use crate::std_types::SourceSpan; +pub use crate::v1_compiler_infer_env::TypeEnv; pub use crate::v1_compiler_infer_types::{ for_each_element_type_node, node_is_element_collection, node_is_keyed_collection, node_type_equals, normalize_access_type_node, resolved_type, @@ -101,6 +102,7 @@ pub fn check_index_access_node( span: Rc, module_name: String, source_indices: Rc>>, + env: Option>, ) -> Rc { { let normed = crate::v1_compiler_infer_types::normalize_access_type_node(base_type.clone()); @@ -110,11 +112,13 @@ pub fn check_index_access_node( normed.clone(), string_type(), source_indices.clone(), + env.clone(), ); let index_is_int = crate::v1_compiler_infer_types::node_type_equals( normed_index.clone(), int_type(), source_indices.clone(), + env.clone(), ); if base_is_string.clone() { { @@ -141,6 +145,7 @@ pub fn check_index_access_node( parts.key_type.clone(), normed_index.clone(), source_indices.clone(), + env.clone(), ) { Rc::new(vec![]) } else { @@ -220,6 +225,7 @@ pub fn check_slice_access_node( span: Rc, module_name: String, source_indices: Rc>>, + env: Option>, ) -> Rc { { let normed_base = @@ -228,6 +234,7 @@ pub fn check_slice_access_node( normed_base.clone(), string_type(), source_indices.clone(), + env.clone(), ); let base_is_list = (crate::std_types::is_ordered_element_collection( crate::v1_std_core::authored_name_at(source_indices.clone(), normed_base.clone()), @@ -250,6 +257,7 @@ pub fn check_slice_access_node( normed_start.clone(), int_type(), source_indices.clone(), + env.clone(), ) { Rc::new(vec![]) } else { @@ -265,6 +273,7 @@ pub fn check_slice_access_node( normed_end.clone(), int_type(), source_indices.clone(), + env.clone(), ) { Rc::new(vec![]) } else { diff --git a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs index b6b6e950a5f..30bfb55945f 100644 --- a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs +++ b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs @@ -660,6 +660,7 @@ pub fn enum_field_type_consistent( crate::v1_compiler_infer_types::child_type_node(field_child.clone()), expected.clone(), source_indices.clone(), + std::option::Option::None, ), std::option::Option::None => false, }) { diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 5553cff0c61..ea02e6b9e43 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -1,13 +1,19 @@ // Generated by v1 compiler -- do not edit. // Source module: v1.compiler.infer_env +use self::AuthoredReferenceReading::*; use self::GlobalBareLookupState::*; use self::UnitVariantPhantomLookup::*; pub use crate::std_algebra::FreeMonoid; -pub use crate::std_coercion::TypeDeclarationProvenance; -use crate::std_coercion::TypeDeclarationProvenance::DeclarationIdentityAbsent; -pub use crate::std_decl_ref::decl_ref; +use crate::std_coercion::TextRepresentation::{ + CodePointSequence, HostText, NotText, TextRepresentationUnidentified, +}; +use crate::std_coercion::TypeDeclarationProvenance::{ + CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, +}; +pub use crate::std_coercion::{TextRepresentation, TypeDeclarationProvenance}; pub use crate::std_decl_ref::DeclarationRef; +pub use crate::std_decl_ref::{decl_ref, declaration_ref_display_key, declaration_ref_eq}; use crate::std_induction::RecursionShape::{ DirectRecursion, ListRecursion, MapValueRecursion, OptionalRecursion, SetRecursion, }; @@ -31,10 +37,10 @@ use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::Cardinality::*; use crate::v1_std_core::CompilerDiagnostic::{AmbiguousReference, UnresolvedType}; -use crate::v1_std_core::Connective::*; +use crate::v1_std_core::Connective::NoConnective; use crate::v1_std_core::DeclarationMarker::Unmarked; use crate::v1_std_core::ExprData::*; -use crate::v1_std_core::InferredNode::Resolved; +use crate::v1_std_core::InferredNode::{CompilerError, Resolved, TypeVariable}; pub use crate::v1_std_core::ParsedModuleItemKind; use crate::v1_std_core::ParsedModuleItemKind::*; pub use crate::v1_std_core::{ @@ -2633,6 +2639,497 @@ pub fn declaration_provenance_of_ref( } } +pub fn text_identity_std_algebra_free_monoid() -> Rc { + crate::std_decl_ref::decl_ref("std.algebra".to_string(), "FreeMonoid".to_string()) +} + +pub fn text_identity_std_types_char() -> Rc { + crate::std_decl_ref::decl_ref("std.types".to_string(), "Char".to_string()) +} + +pub fn node_is_kernel_mint(n: Rc) -> bool { + match (*crate::v1_std_core::declaration_provenance_of(n.clone())).clone() { + TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + } +} + +pub fn node_is_kernel_mint_named(n: Rc, name: String) -> bool { + match (*crate::v1_std_core::declaration_provenance_of(n.clone())).clone() { + TypeDeclarationProvenance::KernelMinted { minted_name: m, .. } => { + (m.clone() == name.clone()) + } + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + } +} + +pub fn authored_declaration_of_type_node( + n: Rc, + env: Rc, +) -> Option> { + match declaration_ref_of_type_node(n.clone(), env.source_indices.clone(), env.clone()) { + Some(d) => Some(d.clone()), + std::option::Option::None => match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: r, .. }) => { + declaration_ref_of_type_node(r.clone(), env.source_indices.clone(), env.clone()) + } + _ => std::option::Option::None, + }, + } +} + +pub fn declaration_binding_of_ref( + d: Rc, + env: Rc, +) -> Option> { + match v1_rt::map_get( + &env.symbol_index.clone().global_bare.clone(), + d.decl_name.clone(), + ) + .as_deref() + .cloned() + { + Some(GlobalBareLookupState::GlobalBareUniqueBinding { + module_path: mp, + binding: b, + .. + }) => { + if (mp.clone() == d.module_path.clone()) { + Some(b.clone()) + } else { + std::option::Option::None + } + } + Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { + candidates: cands, .. + }) => match Rc::new({ + let mut __result = Vec::new(); + for c in cands.iter().cloned() { + if (c.module_path.clone() == d.module_path.clone()) { + __result.push(c); + } + } + __result + }) + .first() + .cloned() + { + Some(c) => Some(c.binding.clone()), + std::option::Option::None => std::option::Option::None, + }, + std::option::Option::None => std::option::Option::None, + } +} + +pub fn text_element_is_char( + n: Rc, + env: Rc, + authoring: Option, +) -> TextRepresentation { + match n.children.clone().first().cloned() { + std::option::Option::None => TextRepresentation::TextRepresentationUnidentified, + Some(ch) => { + let el = match ch.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: r, .. }) => r.clone(), + _ => ch.clone(), + }; + let reading = match authored_declaration_of_type_node(el.clone(), env.clone()) { + Some(e) => Rc::new(AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: e.clone(), + }), + std::option::Option::None => match authoring.clone() { + Some(m) => authored_reference_reading(el.clone(), m.clone(), env.clone()), + std::option::Option::None => { + Rc::new(AuthoredReferenceReading::AuthoredReferenceUnread) + } + }, + }; + match (*reading.clone()).clone() { + AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: e, .. + } => { + if crate::std_decl_ref::declaration_ref_eq( + e.clone(), + text_identity_std_types_char(), + ) { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::NotText + } + } + AuthoredReferenceReading::AuthoredReferenceIsKernel { name: _, .. } => { + TextRepresentation::NotText + } + AuthoredReferenceReading::AuthoredReferenceUnread => { + TextRepresentation::TextRepresentationUnidentified + } + } + } + } +} + +pub fn text_representation_by_identity(n: Rc, env: Rc) -> TextRepresentation { + text_representation_by_identity_bounded( + n.clone(), + env.clone(), + 16, + Rc::new(vec![]), + std::option::Option::None, + ) +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(tag = "_variant")] +pub enum AuthoredReferenceReading { + AuthoredReferenceIsKernel { name: String }, + AuthoredReferenceIsDeclaration { declaration: Rc }, + AuthoredReferenceUnread, +} + +pub fn authored_reference_reading( + n: Rc, + authoring_module: String, + env: Rc, +) -> Rc { + { + let nm = crate::v1_std_core::qualified_last_segment(crate::v1_std_core::authored_name_at( + env.source_indices.clone(), + n.clone(), + )); + if (nm.clone() == "".to_string()) { + Rc::new(AuthoredReferenceReading::AuthoredReferenceUnread) + } else { + { + let local = + match v1_rt::map_get(&env.symbol_index.clone().global_bare.clone(), nm.clone()) + .as_deref() + .cloned() + { + Some(GlobalBareLookupState::GlobalBareUniqueBinding { + module_path: mp, + .. + }) => (mp.clone() == authoring_module.clone()), + Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { + candidates: cands, + .. + }) => { + let mut __found = false; + for c in cands.iter().cloned() { + if (c.module_path.clone() == authoring_module.clone()) { + __found = true; + break; + } + } + __found + } + std::option::Option::None => false, + }; + if local.clone() { + Rc::new(AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: crate::std_decl_ref::decl_ref( + authoring_module.clone(), + nm.clone(), + ), + }) + } else { + if crate::std_types::is_kernel_type(nm.clone()) { + Rc::new(AuthoredReferenceReading::AuthoredReferenceIsKernel { + name: nm.clone(), + }) + } else { + match v1_rt::map_get( + &env.symbol_index.clone().global_bare.clone(), + nm.clone(), + ) + .as_deref() + .cloned() + { + Some(GlobalBareLookupState::GlobalBareUniqueBinding { + module_path: mp, + .. + }) => { + Rc::new(AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: crate::std_decl_ref::decl_ref( + mp.clone(), + nm.clone(), + ), + }) + } + _ => Rc::new(AuthoredReferenceReading::AuthoredReferenceUnread), + } + } + } + } + } + } +} + +pub fn text_representation_by_identity_bounded( + mut __tco_loop_n: Rc, + mut __tco_loop_env: Rc, + mut __tco_loop_fuel: i64, + mut __tco_loop_visited: Rc>, + mut __tco_loop_authoring: Option, +) -> TextRepresentation { + loop { + #[allow(unused_mut)] + let mut n = __tco_loop_n; + #[allow(unused_mut)] + let mut env = __tco_loop_env; + #[allow(unused_mut)] + let mut fuel = __tco_loop_fuel; + #[allow(unused_mut)] + let mut visited = __tco_loop_visited; + #[allow(unused_mut)] + let mut authoring = __tco_loop_authoring; + let resolved = match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: r, .. }) => Some(r.clone()), + _ => std::option::Option::None, + }; + let kernel_string = (node_is_kernel_mint_named(n.clone(), "String".to_string()) + || match resolved.clone() { + Some(r) => node_is_kernel_mint_named(r.clone(), "String".to_string()), + std::option::Option::None => false, + }); + let generic_or_error = match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::TypeVariable { id: _, .. }) => true, + Some(InferredNode::CompilerError { .. }) => true, + _ => false, + }; + let kernel_other = (node_is_kernel_mint(n.clone()) + || match resolved.clone() { + Some(r) => node_is_kernel_mint(r.clone()), + std::option::Option::None => false, + }); + if kernel_string.clone() { + break TextRepresentation::HostText; + } else { + if generic_or_error.clone() { + break TextRepresentation::NotText; + } else { + if (fuel.clone() <= 0) { + break TextRepresentation::TextRepresentationUnidentified; + } else { + if is_where_refinement_node(n.clone()) { + let base_authoring = + match authored_declaration_of_type_node(n.clone(), env.clone()) { + Some(own) => Some(own.module_path.clone()), + std::option::Option::None => authoring.clone(), + }; + match n.children.clone().first().cloned() { + Some(base) => { + let __tco_0 = base.clone(); + let __tco_1 = env; + let __tco_2 = v1_rt::int_sub(fuel, 1); + let __tco_3 = visited; + let __tco_4 = base_authoring.clone(); + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = __tco_3; + __tco_loop_authoring = __tco_4; + continue; + } + std::option::Option::None => { + break TextRepresentation::TextRepresentationUnidentified; + } + } + } else { + let identified = + match authored_declaration_of_type_node(n.clone(), env.clone()) { + Some(d) => Rc::new( + AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: d.clone(), + }, + ), + std::option::Option::None => match authoring.clone() { + Some(m) => authored_reference_reading( + n.clone(), + m.clone(), + env.clone(), + ), + std::option::Option::None => { + Rc::new(AuthoredReferenceReading::AuthoredReferenceUnread) + } + }, + }; + match (*identified.clone()).clone() { + AuthoredReferenceReading::AuthoredReferenceUnread => { + if kernel_other.clone() { + break TextRepresentation::NotText; + } else { + break TextRepresentation::TextRepresentationUnidentified; + } + } + AuthoredReferenceReading::AuthoredReferenceIsKernel { + name: k, .. + } => { + if (k.clone() == "String".to_string()) { + break TextRepresentation::HostText; + } else { + break TextRepresentation::NotText; + } + } + AuthoredReferenceReading::AuthoredReferenceIsDeclaration { + declaration: d, + .. + } => { + if crate::std_decl_ref::declaration_ref_eq( + d.clone(), + text_identity_std_algebra_free_monoid(), + ) { + break text_element_is_char( + n.clone(), + env.clone(), + authoring.clone(), + ); + } else { + let key = + crate::std_decl_ref::declaration_ref_display_key(d.clone()); + if { + let mut __found = false; + for v in visited.iter().cloned() { + if (v.clone() == key.clone()) { + __found = true; + break; + } + } + __found + } { + break TextRepresentation::TextRepresentationUnidentified; + } else { + match declaration_binding_of_ref(d.clone(), env.clone()) { + std::option::Option::None => { + break TextRepresentation::TextRepresentationUnidentified; + } + Some(b) => { + let decl = b.resolved.clone(); + let is_alias = ((decl.connective.clone() + == Connective::NoConnective) + && ((decl.children.clone().len() as i64) == 0)); + if is_where_refinement_node(decl.clone()) { + { + let __tco_0 = decl.clone(); + let __tco_1 = env; + let __tco_2 = v1_rt::int_sub(fuel, 1); + let __tco_3 = v1_rt::concat( + visited, + Rc::new(vec![key.clone()]), + ); + let __tco_4 = Some(d.module_path.clone()); + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = __tco_3; + __tco_loop_authoring = __tco_4; + continue; + } + } else { + if !is_alias.clone() { + break TextRepresentation::NotText; + } else { + match decl + .inferred + .clone() + .as_deref() + .cloned() + { + Some(InferredNode::Resolved { + node: target, + .. + }) => { + let target_is_itself = match authored_declaration_of_type_node(target.clone(), env.clone()) { + Some(t) => crate::std_decl_ref::declaration_ref_eq(t.clone(), d.clone()), + std::option::Option::None => false, +}; + if target_is_itself.clone() { + break TextRepresentation::NotText; + } else { + { + let __tco_0 = + target.clone(); + let __tco_1 = env; + let __tco_2 = + v1_rt::int_sub(fuel, 1); + let __tco_3 = v1_rt::concat( + visited, + Rc::new(vec![ + key.clone() + ]), + ); + let __tco_4 = Some( + d.module_path.clone(), + ); + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = + __tco_3; + __tco_loop_authoring = + __tco_4; + continue; + } + } + } + _ => { + break TextRepresentation::NotText; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn is_where_refinement_node(n: Rc) -> bool { + (((n.connective.clone() == Connective::Conj) + && (n.type_annotation.clone() != std::option::Option::None)) + && ((n.children.clone().len() as i64) == 1)) +} + +pub fn text_representation_is_text_arm(r: TextRepresentation) -> bool { + match r.clone() { + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => true, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, + } +} + +pub fn text_crossing_by_identity(left: Rc, right: Rc, env: Rc) -> bool { + { + let l = text_representation_by_identity(left.clone(), env.clone()); + let r = text_representation_by_identity(right.clone(), env.clone()); + match l.clone() { + TextRepresentation::HostText => match r.clone() { + TextRepresentation::CodePointSequence => true, + TextRepresentation::TextRepresentationUnidentified => false, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::CodePointSequence => match r.clone() { + TextRepresentation::HostText => true, + TextRepresentation::TextRepresentationUnidentified => false, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, + } + } +} + pub fn type_reference_declaration_ref( n: Rc, source_indices: Rc>>, diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index bf719a75827..3e3ca32e85c 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -37,8 +37,7 @@ pub use crate::std_types::{ container_expected_arity, container_param_name, container_template_algebra, container_template_alias_algebra, container_template_alias_rows, is_container_type, }; -pub use crate::v1_compiler_coercion::text_representations_cross; -pub use crate::v1_compiler_infer_env::type_reference_declaration_ref; +pub use crate::v1_compiler_infer_env::{text_crossing_by_identity, type_reference_declaration_ref}; pub use crate::v1_compiler_infer_env::{TypeBinding, TypeEnv}; use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; @@ -2405,10 +2404,26 @@ pub fn node_type_shape( }) } +pub fn text_crossing_in_optional_env( + left: Rc, + right: Rc, + env: Option>, +) -> bool { + match env.clone() { + Some(e) => crate::v1_compiler_infer_env::text_crossing_by_identity( + left.clone(), + right.clone(), + e.clone(), + ), + std::option::Option::None => false, + } +} + pub fn node_type_compatible( left: Rc, right: Rc, source_indices: Rc>>, + env: Option>, ) -> bool { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { let left_err = if (left.inferred.clone() != std::option::Option::None) { @@ -2441,11 +2456,7 @@ pub fn node_type_compatible( if (left_tv.clone() || right_tv.clone()) { true } else { - if crate::v1_compiler_coercion::text_representations_cross( - left.clone(), - right.clone(), - source_indices.clone(), - ) { + if text_crossing_in_optional_env(left.clone(), right.clone(), env.clone()) { false } else { if (left_opt.clone() && right_is_unit.clone()) { @@ -2462,6 +2473,7 @@ pub fn node_type_compatible( a.clone(), b.clone(), source_indices.clone(), + env.clone(), ) }) } else { @@ -2505,6 +2517,7 @@ pub fn node_type_compatible( left_el.clone(), right_el.clone(), source_indices.clone(), + env.clone(), ) } } @@ -2558,6 +2571,7 @@ pub fn node_type_compatible( left_el.clone(), right_el.clone(), source_indices.clone(), + env.clone(), ) } } @@ -2584,6 +2598,7 @@ pub fn node_type_compatible( left_inner.clone(), right_inner.clone(), source_indices.clone(), + env.clone(), ) } } @@ -2687,6 +2702,7 @@ pub fn node_type_equals( left: Rc, right: Rc, source_indices: Rc>>, + env: Option>, ) -> bool { { let left_err = if (left.inferred.clone() != std::option::Option::None) { @@ -2733,12 +2749,14 @@ pub fn node_type_equals( crate::v1_std_core::with_required_cardinality(left.clone()), crate::v1_std_core::with_required_cardinality(right.clone()), source_indices.clone(), + env.clone(), ) } else { node_type_equals_core( left.clone(), right.clone(), source_indices.clone(), + env.clone(), ) } } @@ -2819,12 +2837,9 @@ pub fn node_type_equals_core( left: Rc, right: Rc, source_indices: Rc>>, + env: Option>, ) -> bool { - if crate::v1_compiler_coercion::text_representations_cross( - left.clone(), - right.clone(), - source_indices.clone(), - ) { + if text_crossing_in_optional_env(left.clone(), right.clone(), env.clone()) { false } else { if ((left.connective.clone() == Connective::Arrow) @@ -2832,7 +2847,7 @@ pub fn node_type_equals_core( { ((left.connective.clone() == right.connective.clone()) && callable_signatures_agree(left.clone(), right.clone(), |a, b| { - node_type_equals(a.clone(), b.clone(), source_indices.clone()) + node_type_equals(a.clone(), b.clone(), source_indices.clone(), env.clone()) })) } else { { @@ -2894,6 +2909,7 @@ pub fn node_type_equals_core( pair.1.clone(), right_child.clone(), source_indices.clone(), + env.clone(), ), std::option::Option::None => false, }) { @@ -2939,6 +2955,7 @@ pub fn node_type_equals_core( child_type_node(left_ch.clone()), child_type_node(right_ch.clone()), source_indices.clone(), + env.clone(), ), std::option::Option::None => false, } @@ -2991,6 +3008,7 @@ pub fn node_type_equals_core( right_first.clone(), ), source_indices.clone(), + env.clone(), ) && node_type_equals( child_type_node( left_second.clone(), @@ -3000,6 +3018,7 @@ pub fn node_type_equals_core( .clone(), ), source_indices.clone(), + env.clone(), )) } std::option::Option::None => { @@ -3027,6 +3046,7 @@ pub fn node_type_equals_core( a.clone(), b.clone(), source_indices.clone(), + env.clone(), ) }, ) diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index 972f58e607f..4a1709b93ef 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -626,6 +626,10 @@ pub enum CompilerDiagnostic { method: String, span: Rc, }, + TextRepresentationUnidentifiedAtBoundary { + position: String, + span: Rc, + }, AlgebraApplicationEvidenceUnavailable { receiver_type: String, argument_index: i64, @@ -1006,6 +1010,7 @@ pub fn diagnostic_to_span(d: Rc) -> Rc { CompilerDiagnostic::MethodExistenceUndecided { span: s, .. } => s.clone(), CompilerDiagnostic::MethodExistenceFrontierAdmitted { span: s, .. } => s.clone(), CompilerDiagnostic::ReceiverTypeUnestablished { span: s, .. } => s.clone(), + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { span: s, .. } => s.clone(), CompilerDiagnostic::AlgebraApplicationEvidenceUnavailable { span: s, .. } => s.clone(), CompilerDiagnostic::FrontierOccurrenceBudgetExceeded { span: s, .. } => s.clone(), CompilerDiagnostic::TestCodeReferenced { span: s, .. } => s.clone(), @@ -1085,6 +1090,7 @@ pub fn diagnostic_to_message(d: Rc) -> String { CompilerDiagnostic::MethodExistenceUndecided { method: m, receiver_type: t, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("method '".to_string(), m.clone()), "' cannot be resolved: receiver type '".to_string()), t.clone()), "' establishes no method surface, so the method's existence is not established and no declared frontier row admits it".to_string()), CompilerDiagnostic::MethodExistenceFrontierAdmitted { method: m, receiver_type: t, trigger: tr, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("method '".to_string(), m.clone()), "' on receiver type '".to_string()), t.clone()), "' is admitted by a declared unresolved-method frontier row; dissolves on: ".to_string()), tr.clone()), CompilerDiagnostic::AlgebraApplicationEvidenceUnavailable { receiver_type: t, argument_index: i, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("algebra receiver application evidence unavailable for '".to_string(), t.clone()), "' at argument ".to_string()), (i.clone()).to_string()), ": structural members are not type arguments".to_string()), + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { position: p, .. } => v1_rt::concat(v1_rt::concat("text boundary unjudged at ".to_string(), p.clone()), ": one side is identified text and the other side's type carries no declaration identity the compiler can read, so the text wall makes no verdict here and base type compatibility decides (gunbc.rung_drop text_boundary_identity_wall; trigger: the resolver records declaration identity on every type reference)".to_string()), CompilerDiagnostic::ReceiverTypeUnestablished { .. } => "the receiver's own type was never established, so nothing is known about the method's existence here; this is an upstream type-propagation deficit, not a fact about the method".to_string(), CompilerDiagnostic::FrontierOccurrenceBudgetExceeded { method: m, receiver_type: t, declared: d, observed: o, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("the declared frontier row for '".to_string(), m.clone()), v1_rt::concat("' on receiver type '".to_string(), t.clone())), v1_rt::concat(v1_rt::concat("' no longer matches what this module contains: the row declares ".to_string(), (d.clone()).to_string()), v1_rt::concat(" occurrence(s) and ".to_string(), (o.clone()).to_string()))), " were observed here. If MORE were observed, a new unresolved call has appeared and the receiver's type should be established rather than the count raised. If FEWER were observed, the deficit has partly dissolved and the row must be lowered or deleted so the ratchet keeps its new ground. The count is an equality, not a ceiling, in both directions.".to_string()), CompilerDiagnostic::TestCodeReferenced { referrer: r, target: t, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("'".to_string(), r.clone()), "' references test code '".to_string()), t.clone()), "': a `test` declaration is entered only by the witness runner, so no declaration may call, name or import it. Move shared logic into an ordinary fn, or delete a test that only re-asserts other tests".to_string()), @@ -1236,6 +1242,10 @@ pub fn diagnostic_disposition(d: Rc) -> Rc Rc::new(DiagnosticDisposition { severity: DiagnosticSeverity::SeverityNonError, gate: Rc::new(DiagnosticGateDisposition::GateAdvisoryTypecheck), +}), + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { .. } => Rc::new(DiagnosticDisposition { + severity: DiagnosticSeverity::SeverityNonError, + gate: Rc::new(DiagnosticGateDisposition::GateAdvisoryTypecheck), }), CompilerDiagnostic::FrontierOccurrenceBudgetExceeded { .. } => Rc::new(DiagnosticDisposition { severity: DiagnosticSeverity::SeverityError, From 5235c33988f8ae89a81df7199e31509fa39a8e11 Mon Sep 17 00:00:00 2001 From: "gunbai-bot[bot]" <289086189+gunbai-bot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 02:11:24 +0000 Subject: [PATCH 11/24] chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-rung-drops.md Ledger-Rows-Repaired: docs/design-rung-drops.md text_boundary_identity_wall Heal-Candidate-Run: 36498270119 --- docs/design-rung-drops.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 58cb1ec83fc..15fad840a1a 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -124,7 +124,9 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. -CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: one classifier, v1.compiler.coercion text_representation_of_type (std.coercion TextRepresentation = HostText | CodePointSequence | NotText), is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed), and by the Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type now delegates to it), so the checker cannot admit a pairing the emitter realizes with two carriers. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED, because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT yet this authority: its renderers hold no TypeEnv, so it keeps its provenance-keyed answer, measured to render the self-host crate byte-identically to base, and unifying the two waits on the render paths carrying the env. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. + +STANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text. ### Fabric CI evidence lane as a required merge block, and then as a lane at all — declared 2026-08-31 From 4129275d58f8130b801c5ce7fa59bf54c066ccac Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 29 Sep 2026 08:15:00 +0000 Subject: [PATCH 12/24] Text crossing judgment is a coproduct, not an optional env (review 72427 finding 2) node_type_compatible / node_type_equals / node_type_equals_core and the index/slice access checks take TextJudgment = TextJudgedIn { env } | TextNotAsked { reason }, so every call site that skips the text-identity judgment names why (variant-field summary, callable-component residue, synthetic witness nodes) instead of passing an absent env. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/04_access.dag | 18 ++-- src/v1/04_emit_info.dag | 3 +- src/v1/04_env.dag | 4 + src/v1/04_infer.dag | 33 +++---- src/v1/04_types.dag | 63 ++++++++----- .../stage0/src/bin/infer_semantics_witness.rs | 32 ++++--- src/v1/stage0/src/v1_compiler_infer.rs | 92 ++++++++++--------- src/v1/stage0/src/v1_compiler_infer_access.rs | 22 +++-- .../stage0/src/v1_compiler_infer_emit_info.rs | 9 +- src/v1/stage0/src/v1_compiler_infer_env.rs | 9 ++ src/v1/stage0/src/v1_compiler_infer_types.rs | 82 +++++++++++------ 11 files changed, 218 insertions(+), 149 deletions(-) diff --git a/src/v1/04_access.dag b/src/v1/04_access.dag index 1341fd16060..5ecc5c30d09 100644 --- a/src/v1/04_access.dag +++ b/src/v1/04_access.dag @@ -1,7 +1,6 @@ module v1.compiler.infer_access import std.types { SourceSpan, is_ordered_element_collection } -import v1.compiler.infer_env { TypeEnv } import v1.std.core { Node, NewlineIndex, authored_name_at, @@ -14,6 +13,7 @@ import v1.std.core { } import v1.compiler.infer_types { + TextJudgment, TextJudgedIn, TextNotAsked, TextNotAskedInVariantFieldSummary, TextNotAskedForCallableComponentResidue, resolved_type, normalize_access_type_node, node_type_equals, node_is_keyed_collection, node_is_element_collection, for_each_element_type_node @@ -67,11 +67,11 @@ fn keyed_collection_parts(n: Node, source_indices: Map) -> } } -fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, module_name: String, source_indices: Map, env: TypeEnv?) -> AccessCheckResultNode { +fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, module_name: String, source_indices: Map, text: TextJudgment) -> AccessCheckResultNode { let normed = normalize_access_type_node(n: base_type) let normed_index = normalize_access_type_node(n: index_type) - let base_is_string = node_type_equals(left: normed, right: string_type, source_indices: source_indices, env: env) - let index_is_int = node_type_equals(left: normed_index, right: int_type, source_indices: source_indices, env: env) + let base_is_string = node_type_equals(left: normed, right: string_type, source_indices: source_indices, text: text) + let index_is_int = node_type_equals(left: normed_index, right: int_type, source_indices: source_indices, text: text) if base_is_string { let diags = if index_is_int { [] } else { [access_error(message: "string index requires an Int index", span: span, module_name: module_name)] } @@ -79,7 +79,7 @@ fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, } else { match keyed_collection_parts(n: normed, source_indices: source_indices) { Present { value: parts } => - let key_diags = if node_type_equals(left: parts.key_type, right: normed_index, source_indices: source_indices, env: env) { [] } + let key_diags = if node_type_equals(left: parts.key_type, right: normed_index, source_indices: source_indices, text: text) { [] } else { [access_error(message: "keyed collection index key type does not match the collection key type", span: span, module_name: module_name)] } access_result(inferred: with_optional_cardinality(n: parts.value_type), diagnostics: key_diags, span: span, fallback_message: "invalid keyed collection index access") Absent => @@ -97,18 +97,18 @@ fn check_index_access_node(base_type: Node, index_type: Node, span: SourceSpan, } } -fn check_slice_access_node(base_type: Node, start_type: Node, end_type: Node, span: SourceSpan, module_name: String, source_indices: Map, env: TypeEnv?) -> AccessCheckResultNode { +fn check_slice_access_node(base_type: Node, start_type: Node, end_type: Node, span: SourceSpan, module_name: String, source_indices: Map, text: TextJudgment) -> AccessCheckResultNode { let normed_base = normalize_access_type_node(n: base_type) - let base_is_string = node_type_equals(left: normed_base, right: string_type, source_indices: source_indices, env: env) + let base_is_string = node_type_equals(left: normed_base, right: string_type, source_indices: source_indices, text: text) let base_is_list = is_ordered_element_collection(name: authored_name_at(source_indices: source_indices, node: normed_base)) && node_is_element_collection(n: normed_base, source_indices: source_indices) let base_diags = if base_is_string || base_is_list { [] } else { [access_error(message: "slice is only supported for String and list values", span: span, module_name: module_name)] } let normed_start = normalize_access_type_node(n: start_type) - let start_diags = if node_type_equals(left: normed_start, right: int_type, source_indices: source_indices, env: env) { [] } + let start_diags = if node_type_equals(left: normed_start, right: int_type, source_indices: source_indices, text: text) { [] } else { [access_error(message: "slice start requires an Int index", span: span, module_name: module_name)] } let normed_end = normalize_access_type_node(n: end_type) - let end_diags = if node_type_equals(left: normed_end, right: int_type, source_indices: source_indices, env: env) { [] } + let end_diags = if node_type_equals(left: normed_end, right: int_type, source_indices: source_indices, text: text) { [] } else { [access_error(message: "slice end requires an Int index", span: span, module_name: module_name)] } let all_diags = concat(base_diags, start_diags, end_diags) let slice_result_type = if base_is_string { string_type } diff --git a/src/v1/04_emit_info.dag b/src/v1/04_emit_info.dag index 6a1a72e028d..9400d03d691 100644 --- a/src/v1/04_emit_info.dag +++ b/src/v1/04_emit_info.dag @@ -19,6 +19,7 @@ import v1.std.core { } import v1.compiler.infer_env { TypeEnv, empty_symbol_index, empty_type_env } import v1.compiler.infer_types { + TextJudgment, TextJudgedIn, TextNotAsked, TextNotAskedInVariantFieldSummary, TextNotAskedForCallableComponentResidue, normalize_access_type_node, resolved_type, child_type_node, emit_map_has, node_type_equals } @@ -425,7 +426,7 @@ fn enum_field_present_in_all_variants(variants: List, field_name: String, fn enum_field_type_consistent(variants: List, field_name: String, expected: Node, source_indices: Map) -> Bool { variants |> all(variant => match find_child_named(n: variant, name: field_name, source_indices: source_indices) { - Present { value: field_child } => node_type_equals(left: child_type_node(ch:field_child), right: expected, source_indices: source_indices, env: none) + Present { value: field_child } => node_type_equals(left: child_type_node(ch:field_child), right: expected, source_indices: source_indices, text: TextNotAsked { reason: TextNotAskedInVariantFieldSummary }) Absent => false } ) diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index da16db2d967..f186ff0e985 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -1805,6 +1805,10 @@ fn is_where_refinement_node(n: Node) -> Bool { n.connective == Conj && n.type_annotation != none && (n.children |> count) == 1 } +fn text_representation_is_unidentified(r: TextRepresentation) -> Bool { + match r { TextRepresentationUnidentified => true HostText => false CodePointSequence => false NotText => false } +} + fn text_representation_is_text_arm(r: TextRepresentation) -> Bool { match r { HostText => true CodePointSequence => true NotText => false TextRepresentationUnidentified => false } } diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 4debca787e8..9daa4a8b966 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -95,6 +95,7 @@ import v1.std.core { Unmarked, } import v1.compiler.resolve { ModuleGraph, ResolvedModule, ResolvedImport } import v1.compiler.infer_types { + TextJudgment, TextJudgedIn, TextNotAsked, TextNotAskedInVariantFieldSummary, TextNotAskedForCallableComponentResidue, nominal_type_ref, make_container_type, make_callable_type, KernelTypeBuild, node_is_keyed_collection, node_is_element_collection, node_is_collection, node_is_set_collection, is_fully_resolved, kernel_profile_lookup, @@ -118,7 +119,7 @@ import v1.compiler.infer_method { import v1.compiler.infer_cycle { detect_type_cycles_kahn } import v1.compiler.coercion { provenance_realizes_natively } import v1.compiler.infer_env { - text_representation_by_identity, text_crossing_by_identity, + text_representation_by_identity, text_crossing_by_identity, text_representation_is_text_arm, text_representation_is_unidentified, TypeEnv, TypeBinding, TypeEnvCache, empty_type_env_cache, merge_type_env_cache, merge_type_env_cache_guarded, GuardedTypeEnvCacheMerge, TypeEnvCacheMergeConflict, str_bindings_from_bindings, @@ -1541,7 +1542,7 @@ fn declared_type_conformance_diags_core(declared: Node, produced: Node, span: So )] } else if !both_ground { [] } - else if node_type_compatible(left: declared, right: produced, source_indices: si, env: Present { value: scope.type_env }) { [] } + else if node_type_compatible(left: declared, right: produced, source_indices: si, text: TextJudgedIn { env: scope.type_env }) { [] } else { [type_mismatch_error( expected: node_type_shape(n: declared, source_indices: si), @@ -1883,10 +1884,10 @@ fn set_element_type_is_concrete(elem: Node) -> Bool { } } -fn set_element_types_mismatch(recv_elem: Node, operand_elem: Node, source_indices: Map, env: TypeEnv?) -> Bool { +fn set_element_types_mismatch(recv_elem: Node, operand_elem: Node, source_indices: Map, text: TextJudgment) -> Bool { set_element_type_is_concrete(elem: recv_elem) && set_element_type_is_concrete(elem: operand_elem) - && !node_type_compatible(left: recv_elem, right: operand_elem, source_indices: source_indices, env: env) + && !node_type_compatible(left: recv_elem, right: operand_elem, source_indices: source_indices, text: text) } fn node_admits_list_literal(n: Node, source_indices: Map) -> Bool { @@ -2833,7 +2834,7 @@ fn match_arm_types_are_proven_disjoint(unified_arm_type: Node, arm_type: Node, s conformance_ground_type(n: unified_arm_type, source_indices: source_indices) && conformance_ground_type(n: arm_type, source_indices: source_indices) && node_type_compatible( - left: unified_arm_type, right: arm_type, source_indices: source_indices, env: Present { value: scope.type_env }) == false + left: unified_arm_type, right: arm_type, source_indices: source_indices, text: TextJudgedIn { env: scope.type_env }) == false } } @@ -5363,7 +5364,7 @@ fn callable_component_ground_mismatch( || conformance_ground_type(n: instantiated, source_indices: source_indices) } else if conformance_ground_type(n: formal_part, source_indices: source_indices) && conformance_ground_type(n: instantiated, source_indices: source_indices) { - !node_type_compatible(left: formal_part, right: instantiated, source_indices: source_indices, env: none) + !node_type_compatible(left: formal_part, right: instantiated, source_indices: source_indices, text: TextNotAsked { reason: TextNotAskedForCallableComponentResidue }) } else { false } } } @@ -5835,20 +5836,14 @@ fn argument_text_representation(value: Node, type_env: TypeEnv, module_name: Str text_representation_by_identity(n: expression_value_type(e: value), env: type_env) } -fn text_representation_is_text(r: TextRepresentation) -> Bool { - match r { NotText => false HostText => true CodePointSequence => true TextRepresentationUnidentified => false } -} // THE UNJUDGED POPULATION IS COUNTED, NOT SILENT (gunbc.rung_drop text_boundary_identity_wall). Where // one side of a text boundary is identified text and the other has no readable declaration identity, // the text wall makes no verdict and base compatibility decides; this advisory records each such site, // located, so the whole-corpus compile's advisory census is the instrument that bounds the drop. fn text_unjudged_advisories(a: TextRepresentation, b: TextRepresentation, position: String, span: SourceSpan, module_name: String) -> List { - let a_text = text_representation_is_text(r: a) - let b_text = text_representation_is_text(r: b) - let a_unid = match a { TextRepresentationUnidentified => true _ => false } - let b_unid = match b { TextRepresentationUnidentified => true _ => false } - if (a_text && b_unid) || (b_text && a_unid) { + if (text_representation_is_text_arm(r: a) && text_representation_is_unidentified(r: b)) + || (text_representation_is_text_arm(r: b) && text_representation_is_unidentified(r: a)) { [make_error_node(diagnostic: TextRepresentationUnidentifiedAtBoundary { position: position, span: span }, module_name: module_name)] } else { [] } } @@ -6107,7 +6102,7 @@ fn infer_tier2b_builtin_with_kernel_diags(func_name: String, typed_args: List match operand_elem { Present { value: oe } => - set_element_types_mismatch(recv_elem: re, operand_elem: oe, source_indices: scope.type_env.source_indices, env: Present { value: scope.type_env }) + set_element_types_mismatch(recv_elem: re, operand_elem: oe, source_indices: scope.type_env.source_indices, text: TextJudgedIn { env: scope.type_env }) Absent => false } Absent => false @@ -8598,7 +8593,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu left: then_rt, right: else_rt, source_indices: scope.type_env.source_indices, - env: Present { value: scope.type_env } + text: TextJudgedIn { env: scope.type_env } ) { [] } else { @@ -9028,7 +9023,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu span: span, module_name: scope.module_name, source_indices: scope.type_env.source_indices, - env: Present { value: scope.type_env } + text: TextJudgedIn { env: scope.type_env } ) } _ => none } @@ -9079,7 +9074,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu span: span, module_name: scope.module_name, source_indices: scope.type_env.source_indices, - env: Present { value: scope.type_env } + text: TextJudgedIn { env: scope.type_env } ) } _ => none } @@ -9901,7 +9896,7 @@ fn infer_record_lit_structural(occurrence_identity: NodeOccurrenceIdentity, type if direct_call_formal_has_unbound_type_variable(n: field_conformance_type) { [] } else if node_type_compatible( - left: expected_node, right: got_node, source_indices: scope.type_env.source_indices, env: Present { value: scope.type_env }) { + left: expected_node, right: got_node, source_indices: scope.type_env.source_indices, text: TextJudgedIn { env: scope.type_env }) { [] } else if declared_alias_target_matches_produced( expected: expected_node, produced: got_node, env: scope.type_env) { diff --git a/src/v1/04_types.dag b/src/v1/04_types.dag index 336632f968b..4b358f9364e 100644 --- a/src/v1/04_types.dag +++ b/src/v1/04_types.dag @@ -922,19 +922,32 @@ fn node_type_shape(n: Node, source_indices: Map) -> String } } -// The text judgment asks the corpus declaration index, which lives on the TypeEnv. Every checker -// call site passes its env. The one caller that holds none -- v1.compiler.emit_info's variant -// field-summary builder, which runs without a census and asks whether a shared field has one type, -// not whether a value crosses a representation boundary -- passes none, and the judgment is not -// asked there (declared on gunbc.rung_drop text_boundary_identity_wall). -fn text_crossing_in_optional_env(left: Node, right: Node, env: TypeEnv?) -> Bool { - match env { - Present { value: e } => text_crossing_by_identity(left: left, right: right, env: e) - Absent => false +// The text judgment asks the corpus declaration index, which lives on the TypeEnv. The callers that do +// not ask are: v1.compiler.emit_info's variant field-summary builder (no census, and it asks whether a +// shared field has one type, not whether a value crosses a boundary); the callable-component ground +// check (callable values are the declared residue of gunbc.rung_drop text_boundary_identity_wall); +// and the witness binary's synthetic nodes. +// WHETHER A COMPARISON ASKS THE TEXT JUDGMENT IS A CLOSED CHOICE, NOT AN OPTIONAL ENV (review 72427). +// Every checker site passes TextJudgedIn with its env. A caller that does not ask must name WHY +// through a declared TextNotAskedReason; adding a caller that skips the wall means adding a variant +// here, where review sees it, never passing an absent value. +type TextJudgment + = TextJudgedIn { env: TypeEnv } + | TextNotAsked { reason: TextNotAskedReason } + +type TextNotAskedReason + = TextNotAskedInVariantFieldSummary + | TextNotAskedForCallableComponentResidue + | TextNotAskedForSyntheticWitnessNodes + +fn text_crossing_in_judgment(left: Node, right: Node, text: TextJudgment) -> Bool { + match text { + TextJudgedIn { env: e } => text_crossing_by_identity(left: left, right: right, env: e) + TextNotAsked { reason: _ } => false } } -fn node_type_compatible(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { +fn node_type_compatible(left: Node, right: Node, source_indices: Map, text: TextJudgment) -> Bool { let left_err = if left.inferred != none { is_compiler_error(inferred: left.inferred.value) } else { false } let right_err = if right.inferred != none { is_compiler_error(inferred: right.inferred.value) } else { false } let left_tv = if left.inferred != none { is_type_variable(inferred: left.inferred.value) } else { false } @@ -945,11 +958,11 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map node_type_compatible(left: a, right: b, source_indices: source_indices, env: env)) + callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_compatible(left: a, right: b, source_indices: source_indices, text: text)) } else { let left_is_container = node_is_element_collection(n: left, source_indices: source_indices) @@ -966,7 +979,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map true } Absent => true @@ -986,7 +999,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map true } Absent => true @@ -998,7 +1011,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { +fn node_type_equals(left: Node, right: Node, source_indices: Map, text: TextJudgment) -> Bool { let left_err = if left.inferred != none { is_compiler_error(inferred: left.inferred.value) } else { false } let right_err = if right.inferred != none { is_compiler_error(inferred: right.inferred.value) } else { false } let left_tv = if left.inferred != none { is_type_variable(inferred: left.inferred.value) } else { false } @@ -1054,10 +1067,10 @@ fn node_type_equals(left: Node, right: Node, source_indices: Map B } } -fn node_type_equals_core(left: Node, right: Node, source_indices: Map, env: TypeEnv?) -> Bool { - if text_crossing_in_optional_env(left: left, right: right, env: env) { +fn node_type_equals_core(left: Node, right: Node, source_indices: Map, text: TextJudgment) -> Bool { + if text_crossing_in_judgment(left: left, right: right, text: text) { false } else if left.connective == Arrow || (right.connective == Arrow) { left.connective == right.connective - && callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, env: env)) + && callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, text: text)) } else { let left_leaf = left.connective == NoConnective && left.children |> count == 0 && left.properties |> count == 0 let right_leaf = right.connective == NoConnective && right.children |> count == 0 && right.properties |> count == 0 @@ -1129,7 +1142,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map enumerate |> all(pair => match right.children |> skip(pair.first) |> first { - Present { value: right_child } => node_type_equals(left: pair.second, right: right_child, source_indices: source_indices, env: env) + Present { value: right_child } => node_type_equals(left: pair.second, right: right_child, source_indices: source_indices, text: text) Absent => false } ) @@ -1150,7 +1163,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map first { Present { value: left_ch } => match right.children |> first { - Present { value: right_ch } => node_type_equals(left: child_type_node(ch: left_ch), right: child_type_node(ch: right_ch), source_indices: source_indices, env: env) + Present { value: right_ch } => node_type_equals(left: child_type_node(ch: left_ch), right: child_type_node(ch: right_ch), source_indices: source_indices, text: text) Absent => false } Absent => false @@ -1169,7 +1182,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map match right.children |> skip(1) |> first { Present { value: right_second } => - node_type_equals(left: child_type_node(ch: left_first), right: child_type_node(ch: right_first), source_indices: source_indices, env: env) && node_type_equals(left: child_type_node(ch: left_second), right: child_type_node(ch: right_second), source_indices: source_indices, env: env) + node_type_equals(left: child_type_node(ch: left_first), right: child_type_node(ch: right_first), source_indices: source_indices, text: text) && node_type_equals(left: child_type_node(ch: left_second), right: child_type_node(ch: right_second), source_indices: source_indices, text: text) Absent => false } Absent => false @@ -1183,7 +1196,7 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map count > 0 && right.params |> count > 0 { - callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, env: env)) + callable_signatures_agree(left: left, right: right, agree: (a, b) => node_type_equals(left: a, right: b, source_indices: source_indices, text: text)) } else { false diff --git a/src/v1/stage0/src/bin/infer_semantics_witness.rs b/src/v1/stage0/src/bin/infer_semantics_witness.rs index a88bfad6107..703453df93f 100644 --- a/src/v1/stage0/src/bin/infer_semantics_witness.rs +++ b/src/v1/stage0/src/bin/infer_semantics_witness.rs @@ -542,7 +542,11 @@ type AccountId = Refined user_id.clone(), account_id, result.source_indices.clone(), - Some(module.type_env.clone()) + Rc::new( + v1_compiler::v1_compiler_infer_types::TextJudgment::TextJudgedIn { + env: module.type_env.clone() + } + ) ), "PD-3: node_type_compatible must reject brand-twin UserId-for-AccountId" ); @@ -551,7 +555,11 @@ type AccountId = Refined user_id.clone(), user_id, result.source_indices.clone(), - Some(module.type_env.clone()) + Rc::new( + v1_compiler::v1_compiler_infer_types::TextJudgment::TextJudgedIn { + env: module.type_env.clone() + } + ) ), "PD-3: node_type_compatible must accept same-brand UserId-for-UserId" ); @@ -634,7 +642,7 @@ fn list_int_index_returns_optional_element_type() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert_eq!( @@ -655,7 +663,7 @@ fn malformed_map_index_returns_compiler_error_type() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert_eq!(result.diagnostics.len(), 1); @@ -679,7 +687,7 @@ fn invalid_slice_returns_compiler_error_type() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert_eq!(result.diagnostics.len(), 1); @@ -698,7 +706,7 @@ fn valid_list_slice_preserves_list_type() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert!(result.diagnostics.is_empty()); @@ -725,7 +733,7 @@ fn valid_map_index_preserves_optional_value_type() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert!(result.diagnostics.is_empty()); @@ -1859,7 +1867,7 @@ fn map_index_with_correct_key_type_succeeds() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert!( result.diagnostics.is_empty(), @@ -1886,7 +1894,7 @@ fn map_index_with_wrong_key_type_reports_error() { zero_span(), "test".to_string(), empty_source_indices(), - None, + Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes }), ); assert_eq!( result.diagnostics.len(), @@ -1949,7 +1957,7 @@ fn list_and_freemonoid_compatible_same_element() { let list_sym = container_node("List".to_string(), leaf_node("Symbol".to_string())); let fm_sym = container_node("FreeMonoid".to_string(), leaf_node("Symbol".to_string())); assert!( - node_type_compatible(list_sym, fm_sym, empty_source_indices(), None), + node_type_compatible(list_sym, fm_sym, empty_source_indices(), Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes })), "List and FreeMonoid are declared aliases — must be compatible at type-comparison" ); } @@ -1958,7 +1966,7 @@ fn list_and_freemonoid_incompatible_different_element() { let list_int = container_node("List".to_string(), leaf_node("Int".to_string())); let fm_string = container_node("FreeMonoid".to_string(), leaf_node("String".to_string())); assert!( - !node_type_compatible(list_int, fm_string, empty_source_indices(), None), + !node_type_compatible(list_int, fm_string, empty_source_indices(), Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes })), "List vs FreeMonoid differ in element type — must stay incompatible" ); } @@ -1967,7 +1975,7 @@ fn list_freemonoid_compat_is_symmetric() { let fm_sym = container_node("FreeMonoid".to_string(), leaf_node("Symbol".to_string())); let list_sym = container_node("List".to_string(), leaf_node("Symbol".to_string())); assert!( - node_type_compatible(fm_sym, list_sym, empty_source_indices(), None), + node_type_compatible(fm_sym, list_sym, empty_source_indices(), Rc::new(v1_compiler::v1_compiler_infer_types::TextJudgment::TextNotAsked { reason: v1_compiler::v1_compiler_infer_types::TextNotAskedReason::TextNotAskedForSyntheticWitnessNodes })), "alias compatibility must hold in both argument orders" ); } diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 70a634299bd..f422ed5a67d 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -119,7 +119,8 @@ pub use crate::v1_compiler_infer_env::{ qualified_all_but_last, qualify_borrowed_inferred, qualify_borrowed_type_names, qualify_decl_reference_positions, str_bindings_from_bindings, symbol_index_insert, symbol_index_insert_decl, symbol_index_insert_service, symbol_index_lookup, - text_crossing_by_identity, text_representation_by_identity, type_reference_declaration, + text_crossing_by_identity, text_representation_by_identity, text_representation_is_text_arm, + text_representation_is_unidentified, type_reference_declaration, type_reference_declaration_ref, unit_variant_index_shadow_insert, }; pub use crate::v1_compiler_infer_env::{ @@ -211,7 +212,10 @@ pub use crate::v1_compiler_infer_sigs::{ CallableCandidate, CallableIdentity, DerivedCalleeSig, FuncSigLookup, NoDerivableSigReason, ResolveFuncSigsResult, ResolvedFormals, ResolvedFuncEnv, ResolvedFuncSig, }; -pub use crate::v1_compiler_infer_types::KernelTypeBuild; +use crate::v1_compiler_infer_types::TextJudgment::{TextJudgedIn, TextNotAsked}; +use crate::v1_compiler_infer_types::TextNotAskedReason::{ + TextNotAskedForCallableComponentResidue, TextNotAskedInVariantFieldSummary, +}; pub use crate::v1_compiler_infer_types::{ bare_map_node, bare_set_node, callable_inferred, callable_return_type, child_type_node, emit_map_has, extract_optional_inner_node, for_each_element_type_node, infer_binop_type_node, @@ -224,6 +228,7 @@ pub use crate::v1_compiler_infer_types::{ structural_carrier_template_name, subtraction_refinement_of, template_return_has_variables, template_return_is_receiver_self, }; +pub use crate::v1_compiler_infer_types::{KernelTypeBuild, TextJudgment, TextNotAskedReason}; pub use crate::v1_compiler_resolve::{ModuleGraph, ResolvedImport, ResolvedModule}; use crate::v1_compiler_type_head_exposure::TypeHeadExposure::{ ExposedTypeHead, MalformedApplicationHead, OpaqueTypeHead, StuckTypeHead, @@ -2099,7 +2104,9 @@ pub fn declared_type_conformance_diags_core( declared.clone(), produced.clone(), si.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) { Rc::new(vec![]) } else { @@ -2675,7 +2682,7 @@ pub fn set_element_types_mismatch( recv_elem: Rc, operand_elem: Rc, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> bool { ((set_element_type_is_concrete(recv_elem.clone()) && set_element_type_is_concrete(operand_elem.clone())) @@ -2683,7 +2690,7 @@ pub fn set_element_types_mismatch( recv_elem.clone(), operand_elem.clone(), source_indices.clone(), - env.clone(), + text.clone(), )) } @@ -3837,7 +3844,9 @@ pub fn match_arm_types_are_proven_disjoint( unified_arm_type.clone(), arm_type.clone(), source_indices.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) == false)) } } @@ -7461,7 +7470,9 @@ pub fn callable_component_ground_mismatch( formal_part.clone(), instantiated.clone(), source_indices.clone(), - std::option::Option::None, + Rc::new(TextJudgment::TextNotAsked { + reason: TextNotAskedReason::TextNotAskedForCallableComponentResidue, + }), ) } else { false @@ -8413,15 +8424,6 @@ pub fn argument_text_representation( ) } -pub fn text_representation_is_text(r: TextRepresentation) -> bool { - match r.clone() { - TextRepresentation::NotText => false, - TextRepresentation::HostText => true, - TextRepresentation::CodePointSequence => true, - TextRepresentation::TextRepresentationUnidentified => false, - } -} - pub fn text_unjudged_advisories( a: TextRepresentation, b: TextRepresentation, @@ -8429,30 +8431,22 @@ pub fn text_unjudged_advisories( span: Rc, module_name: String, ) -> Rc>> { + if ((crate::v1_compiler_infer_env::text_representation_is_text_arm(a.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(b.clone())) + || (crate::v1_compiler_infer_env::text_representation_is_text_arm(b.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(a.clone()))) { - let a_text = text_representation_is_text(a.clone()); - let b_text = text_representation_is_text(b.clone()); - let a_unid = match a.clone() { - TextRepresentation::TextRepresentationUnidentified => true, - _ => false, - }; - let b_unid = match b.clone() { - TextRepresentation::TextRepresentationUnidentified => true, - _ => false, - }; - if ((a_text.clone() && b_unid.clone()) || (b_text.clone() && a_unid.clone())) { - Rc::new(vec![crate::v1_std_core::make_error_node( - Rc::new( - CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { - position: position.clone(), - span: span.clone(), - }, - ), - module_name.clone(), - )]) - } else { - Rc::new(vec![]) - } + Rc::new(vec![crate::v1_std_core::make_error_node( + Rc::new( + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: position.clone(), + span: span.clone(), + }, + ), + module_name.clone(), + )]) + } else { + Rc::new(vec![]) } } @@ -8875,7 +8869,9 @@ pub fn infer_tier2b_builtin_with_kernel_diags( re.clone(), oe.clone(), scope.type_env.clone().source_indices.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ), std::option::Option::None => false, }, @@ -13511,7 +13507,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), then_rt.clone(), else_rt.clone(), scope.type_env.clone().source_indices.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) { Rc::new(vec![]) } else { @@ -14477,7 +14475,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )), _ => std::option::Option::None, }, @@ -14545,7 +14545,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), - Some(scope.type_env.clone()), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )) } _ => std::option::Option::None, @@ -15988,7 +15990,9 @@ let field_type_diags = match field_declared_type.clone() { if direct_call_formal_has_unbound_type_variable(field_conformance_type.clone()) { Rc::new(vec![]) } else { - if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone(), Some(scope.type_env.clone())) { + if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone(), Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), +})) { Rc::new(vec![]) } else { if declared_alias_target_matches_produced(expected_node.clone(), got_node.clone(), scope.type_env.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_infer_access.rs b/src/v1/stage0/src/v1_compiler_infer_access.rs index 297be8d3ef8..135f7d12523 100644 --- a/src/v1/stage0/src/v1_compiler_infer_access.rs +++ b/src/v1/stage0/src/v1_compiler_infer_access.rs @@ -3,11 +3,15 @@ pub use crate::std_types::is_ordered_element_collection; pub use crate::std_types::SourceSpan; -pub use crate::v1_compiler_infer_env::TypeEnv; +use crate::v1_compiler_infer_types::TextJudgment::{TextJudgedIn, TextNotAsked}; +use crate::v1_compiler_infer_types::TextNotAskedReason::{ + TextNotAskedForCallableComponentResidue, TextNotAskedInVariantFieldSummary, +}; pub use crate::v1_compiler_infer_types::{ for_each_element_type_node, node_is_element_collection, node_is_keyed_collection, node_type_equals, normalize_access_type_node, resolved_type, }; +pub use crate::v1_compiler_infer_types::{TextJudgment, TextNotAskedReason}; use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::CompilerDiagnostic::InternalError; @@ -102,7 +106,7 @@ pub fn check_index_access_node( span: Rc, module_name: String, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> Rc { { let normed = crate::v1_compiler_infer_types::normalize_access_type_node(base_type.clone()); @@ -112,13 +116,13 @@ pub fn check_index_access_node( normed.clone(), string_type(), source_indices.clone(), - env.clone(), + text.clone(), ); let index_is_int = crate::v1_compiler_infer_types::node_type_equals( normed_index.clone(), int_type(), source_indices.clone(), - env.clone(), + text.clone(), ); if base_is_string.clone() { { @@ -145,7 +149,7 @@ pub fn check_index_access_node( parts.key_type.clone(), normed_index.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) { Rc::new(vec![]) } else { @@ -225,7 +229,7 @@ pub fn check_slice_access_node( span: Rc, module_name: String, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> Rc { { let normed_base = @@ -234,7 +238,7 @@ pub fn check_slice_access_node( normed_base.clone(), string_type(), source_indices.clone(), - env.clone(), + text.clone(), ); let base_is_list = (crate::std_types::is_ordered_element_collection( crate::v1_std_core::authored_name_at(source_indices.clone(), normed_base.clone()), @@ -257,7 +261,7 @@ pub fn check_slice_access_node( normed_start.clone(), int_type(), source_indices.clone(), - env.clone(), + text.clone(), ) { Rc::new(vec![]) } else { @@ -273,7 +277,7 @@ pub fn check_slice_access_node( normed_end.clone(), int_type(), source_indices.clone(), - env.clone(), + text.clone(), ) { Rc::new(vec![]) } else { diff --git a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs index 30bfb55945f..9a26dc82d98 100644 --- a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs +++ b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs @@ -11,9 +11,14 @@ use crate::v1_compiler_artifact::RenderTarget::Rust; pub use crate::v1_compiler_coercion::{declaration_realization, realized_checkpoint}; pub use crate::v1_compiler_infer_env::TypeEnv; pub use crate::v1_compiler_infer_env::{empty_symbol_index, empty_type_env}; +use crate::v1_compiler_infer_types::TextJudgment::{TextJudgedIn, TextNotAsked}; +use crate::v1_compiler_infer_types::TextNotAskedReason::{ + TextNotAskedForCallableComponentResidue, TextNotAskedInVariantFieldSummary, +}; pub use crate::v1_compiler_infer_types::{ child_type_node, emit_map_has, node_type_equals, normalize_access_type_node, resolved_type, }; +pub use crate::v1_compiler_infer_types::{TextJudgment, TextNotAskedReason}; use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::Cardinality::CardOptional; @@ -660,7 +665,9 @@ pub fn enum_field_type_consistent( crate::v1_compiler_infer_types::child_type_node(field_child.clone()), expected.clone(), source_indices.clone(), - std::option::Option::None, + Rc::new(TextJudgment::TextNotAsked { + reason: TextNotAskedReason::TextNotAskedInVariantFieldSummary, + }), ), std::option::Option::None => false, }) { diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index ea02e6b9e43..6bcb7ef4b8d 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -3098,6 +3098,15 @@ pub fn is_where_refinement_node(n: Rc) -> bool { && ((n.children.clone().len() as i64) == 1)) } +pub fn text_representation_is_unidentified(r: TextRepresentation) -> bool { + match r.clone() { + TextRepresentation::TextRepresentationUnidentified => true, + TextRepresentation::HostText => false, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + } +} + pub fn text_representation_is_text_arm(r: TextRepresentation) -> bool { match r.clone() { TextRepresentation::HostText => true, diff --git a/src/v1/stage0/src/v1_compiler_infer_types.rs b/src/v1/stage0/src/v1_compiler_infer_types.rs index 3e3ca32e85c..3252a0d440e 100644 --- a/src/v1/stage0/src/v1_compiler_infer_types.rs +++ b/src/v1/stage0/src/v1_compiler_infer_types.rs @@ -1,6 +1,8 @@ // Generated by v1 compiler -- do not edit. // Source module: v1.compiler.infer_types +use self::TextJudgment::*; +use self::TextNotAskedReason::*; use self::TypeResolutionVerdict::*; use crate::std_algebra::AlgebraProfile::{ ApproximateFieldProfile, BooleanAlgebraProfile, FinitePowerSetProfile, @@ -2404,18 +2406,33 @@ pub fn node_type_shape( }) } -pub fn text_crossing_in_optional_env( - left: Rc, - right: Rc, - env: Option>, -) -> bool { - match env.clone() { - Some(e) => crate::v1_compiler_infer_env::text_crossing_by_identity( - left.clone(), - right.clone(), - e.clone(), - ), - std::option::Option::None => false, +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(tag = "_variant")] +pub enum TextJudgment { + TextJudgedIn { env: Rc }, + TextNotAsked { reason: TextNotAskedReason }, +} + +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +#[serde(tag = "_variant")] +pub enum TextNotAskedReason { + TextNotAskedInVariantFieldSummary, + TextNotAskedForCallableComponentResidue, + TextNotAskedForSyntheticWitnessNodes, +} + +pub fn text_crossing_in_judgment(left: Rc, right: Rc, text: Rc) -> bool { + match (*text.clone()).clone() { + TextJudgment::TextJudgedIn { env: e, .. } => { + crate::v1_compiler_infer_env::text_crossing_by_identity( + left.clone(), + right.clone(), + e.clone(), + ) + } + TextJudgment::TextNotAsked { reason: _, .. } => false, } } @@ -2423,7 +2440,7 @@ pub fn node_type_compatible( left: Rc, right: Rc, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> bool { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { let left_err = if (left.inferred.clone() != std::option::Option::None) { @@ -2456,7 +2473,7 @@ pub fn node_type_compatible( if (left_tv.clone() || right_tv.clone()) { true } else { - if text_crossing_in_optional_env(left.clone(), right.clone(), env.clone()) { + if text_crossing_in_judgment(left.clone(), right.clone(), text.clone()) { false } else { if (left_opt.clone() && right_is_unit.clone()) { @@ -2473,7 +2490,7 @@ pub fn node_type_compatible( a.clone(), b.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) }) } else { @@ -2517,7 +2534,7 @@ pub fn node_type_compatible( left_el.clone(), right_el.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) } } @@ -2571,7 +2588,7 @@ pub fn node_type_compatible( left_el.clone(), right_el.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) } } @@ -2598,7 +2615,7 @@ pub fn node_type_compatible( left_inner.clone(), right_inner.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) } } @@ -2702,7 +2719,7 @@ pub fn node_type_equals( left: Rc, right: Rc, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> bool { { let left_err = if (left.inferred.clone() != std::option::Option::None) { @@ -2749,14 +2766,14 @@ pub fn node_type_equals( crate::v1_std_core::with_required_cardinality(left.clone()), crate::v1_std_core::with_required_cardinality(right.clone()), source_indices.clone(), - env.clone(), + text.clone(), ) } else { node_type_equals_core( left.clone(), right.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) } } @@ -2837,9 +2854,9 @@ pub fn node_type_equals_core( left: Rc, right: Rc, source_indices: Rc>>, - env: Option>, + text: Rc, ) -> bool { - if text_crossing_in_optional_env(left.clone(), right.clone(), env.clone()) { + if text_crossing_in_judgment(left.clone(), right.clone(), text.clone()) { false } else { if ((left.connective.clone() == Connective::Arrow) @@ -2847,7 +2864,7 @@ pub fn node_type_equals_core( { ((left.connective.clone() == right.connective.clone()) && callable_signatures_agree(left.clone(), right.clone(), |a, b| { - node_type_equals(a.clone(), b.clone(), source_indices.clone(), env.clone()) + node_type_equals(a.clone(), b.clone(), source_indices.clone(), text.clone()) })) } else { { @@ -2909,7 +2926,7 @@ pub fn node_type_equals_core( pair.1.clone(), right_child.clone(), source_indices.clone(), - env.clone(), + text.clone(), ), std::option::Option::None => false, }) { @@ -2955,7 +2972,7 @@ pub fn node_type_equals_core( child_type_node(left_ch.clone()), child_type_node(right_ch.clone()), source_indices.clone(), - env.clone(), + text.clone(), ), std::option::Option::None => false, } @@ -3008,7 +3025,7 @@ pub fn node_type_equals_core( right_first.clone(), ), source_indices.clone(), - env.clone(), + text.clone(), ) && node_type_equals( child_type_node( left_second.clone(), @@ -3018,7 +3035,7 @@ pub fn node_type_equals_core( .clone(), ), source_indices.clone(), - env.clone(), + text.clone(), )) } std::option::Option::None => { @@ -3046,7 +3063,7 @@ pub fn node_type_equals_core( a.clone(), b.clone(), source_indices.clone(), - env.clone(), + text.clone(), ) }, ) @@ -3540,3 +3557,10 @@ pub fn emit_map_has(m: Rc>, key: String) -> bool { std::option::Option::None => false, } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct TextNotAskedInVariantFieldSummary; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct TextNotAskedForCallableComponentResidue; +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct TextNotAskedForSyntheticWitnessNodes; From 0f67a6006298ded90b2554d35b50f2b10f7261fb Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Tue, 29 Sep 2026 20:35:08 +0000 Subject: [PATCH 13/24] Text wall row states its current standing; renderer's text answer declared as a stall (review 72772) - gunbc.rung_drop text_boundary_identity_wall opens with its current standing (identified crossings structurally guaranteed; two remaining populations: callable-signature crossings and the unidentified population). The 2026-08-30 declaration is kept as labelled history. - gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall: the Rust renderer's provenance-keyed is_host_text_carrier_type is a second authority for 'is this host text'. Nothing fell, so it is a 4b(2) stall (current Mitigatable, ceiling StructurallyImpossible), with its population and a trigger that names the capability (render paths read the classifier through the env they were checked in, sufficient to delete it with no fallback arm). Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/guarantee_stall/roster.dag | 2 ++ ...ier_render_not_keyed_on_identity_stall.dag | 24 +++++++++++++++++++ .../rung_drop/text_boundary_identity_wall.dag | 2 +- docs/design-rung-drops.md | 6 +++-- 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 dag/gunbc/guarantee_stall/text_carrier_render_not_keyed_on_identity_stall.dag diff --git a/dag/gunbc/guarantee_stall/roster.dag b/dag/gunbc/guarantee_stall/roster.dag index 61d866827c1..1f3ffb51e93 100644 --- a/dag/gunbc/guarantee_stall/roster.dag +++ b/dag/gunbc/guarantee_stall/roster.dag @@ -69,6 +69,7 @@ import gunbc.guarantee_stall.information_retrieval_transport_cannot_report_statu import gunbc.guarantee_stall.module_cited_only_in_annotation_prose_stall { module_cited_only_in_annotation_prose_stall } import gunbc.guarantee_stall.joint_claim_join_private_corpus_unindexed_stall { joint_claim_join_private_corpus_unindexed_stall } import gunbc.guarantee_stall.coercion_two_algebras_answer_one_question_stall { coercion_two_algebras_answer_one_question_stall } +import gunbc.guarantee_stall.text_carrier_render_not_keyed_on_identity_stall { text_carrier_render_not_keyed_on_identity_stall } import gunbc.guarantee_stall.emitted_inline_crate_path_edges_unreturned_stall { emitted_inline_crate_path_edges_unreturned_stall } // THE COHORT PROVENANCE NOTES BELOW WERE AUTHORED WHEN EVERY ROW SAT IN ONE FILE, and they are @@ -234,6 +235,7 @@ data all_guarantee_stalls: List = [ pci_address_rendering_equivalence_stall, fabric_purpose_handover_registration_unobserved_stall, coercion_two_algebras_answer_one_question_stall, + text_carrier_render_not_keyed_on_identity_stall, emitted_inline_crate_path_edges_unreturned_stall, ] diff --git a/dag/gunbc/guarantee_stall/text_carrier_render_not_keyed_on_identity_stall.dag b/dag/gunbc/guarantee_stall/text_carrier_render_not_keyed_on_identity_stall.dag new file mode 100644 index 00000000000..7d01fd981f0 --- /dev/null +++ b/dag/gunbc/guarantee_stall/text_carrier_render_not_keyed_on_identity_stall.dag @@ -0,0 +1,24 @@ +module gunbc.guarantee_stall.text_carrier_render_not_keyed_on_identity_stall + +import gunbc.guarantee_rung { Mitigatable, StructurallyImpossible } +import gunbc.guarantee_stall { GuaranteeStall, AwaitsOneGrounding, BoundedPopulation, climbs_when } + +data text_carrier_render_not_keyed_on_identity_stall: GuaranteeStall = GuaranteeStall { + subject: "Two authorities answer whether a type is host text. The checker decides it by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity, gunbc.rung_drop text_boundary_identity_wall); the Rust renderer decides which carrier to emit by the authored spelling plus kernel provenance (v1.compiler.emit_rust is_host_text_carrier_type). Nothing forces the two answers to agree: on the self-host path a disagreement surfaces only as a rustc type mismatch in the emitted-crate build, and on any other emission nothing observes it. Nothing fell -- the renderer was provenance-keyed before the checker climbed -- so this is a stall below its ceiling, not a drop (review 72772).", + current: Mitigatable, + ceiling: StructurallyImpossible, + blocker: AwaitsOneGrounding { grounding: "the Rust render paths carry the TypeEnv the checker judged with; four of the deciding paths (v1.compiler.emit_rust render_rust_type, render_rust_type_without_applied_binding, rust_operand_realization_of_type, data_def_annotation_is_named_refinement) take no env, so they cannot ask the identity classifier" }, + population: BoundedPopulation { members: [ + "v1.compiler.infer_env.text_representation_by_identity", + "v1.compiler.emit_rust.is_host_text_carrier_type", + "v1.compiler.emit_rust.render_rust_type", + "v1.compiler.emit_rust.render_rust_applied_type", + "v1.compiler.emit_rust.render_rust_decl_type", + "v1.compiler.emit_rust.render_rust_fn_sig_type", + "v1.compiler.emit_rust.render_rust_type_with_applied_binding_in_scope", + "v1.compiler.emit_rust.render_rust_type_without_applied_binding", + "v1.compiler.emit_rust.rust_operand_realization_of_type", + "v1.compiler.emit_rust.data_def_annotation_is_named_refinement" + ] }, + next_rung_trigger: climbs_when(capability: "every Rust render path that chooses a text carrier reads v1.compiler.infer_env text_representation_by_identity through the TypeEnv it was checked in, SUFFICIENT FOR is_host_text_carrier_type to be deleted with no fallback arm: an unidentified type refuses at render rather than defaulting to a carrier, which in turn needs gunbc.rung_drop text_boundary_identity_wall's resolver trigger (declaration identity on every type reference). Routing only the renderers that already hold an env does not retire this row.") +} diff --git a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag index dd38297e851..0efd759e9a6 100644 --- a/dag/gunbc/rung_drop/text_boundary_identity_wall.dag +++ b/dag/gunbc/rung_drop/text_boundary_identity_wall.dag @@ -12,5 +12,5 @@ data text_boundary_identity_wall: RungDrop = RungDrop { standing: Standing, - declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT yet this authority: its renderers hold no TypeEnv, so it keeps its provenance-keyed answer, measured to render the self-host crate byte-identically to base, and unifying the two waits on the render paths carrying the env. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here.\n\nSTANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. The identity each row reports as unread is v1's ONE type-reference derivation, `v1.compiler.infer_env` `type_reference_declaration_reading` (its `TypeReferenceUnidentified` arm), the same read `v1.tests.claim.carrier_realization_census` records per authored reference position; the census is NOT this population's producer, because it enumerates authored type-reference positions inside declarations, while this population is checker comparisons at use sites whose value side is usually an inferred type with no authored position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text." } + declaration: AuthoredProse { legacy: TextBoundaryIdentityWall, authored: "**CURRENT STANDING (2026-09-29, after the CLIMB below): a text crossing between two IDENTIFIED sides is structurally guaranteed -- a non-literal kernel-String value at an identified code-point-sequence boundary unfolds through the declared row or refuses, located. This row stands for exactly two remaining populations, each with its own trigger stated below: (1) CALLABLE-SIGNATURE crossings -- a callable whose parameters are code-point sequences passed where a callable over host text is expected, which callable-signature agreement does not yet judge by the text classifier; (2) the UNIDENTIFIED population -- a boundary where one side has no readable declaration identity, which the wall declines to judge (STANDING RESTATED, at the end). The Rust renderer's separate text-carrier answer is its own row, gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall.**\n\nHISTORY, AS DECLARED 2026-08-30 (the structural-text landing), superseded by the CLIMB below -- kept as history, not as standing: **A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DID NOT REFUSE.** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINED ADMITTED at that boundary (until the CLIMB) and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class.\n\nPOPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses.\n\nSO THE PEEL IS NOT REACHED ONCE AN ALIAS STANDS IN THE POSITION, which is a different defect from the peel keying on the wrong identity. Naming the guarded declaration THROUGH an alias does not preserve the refusal. The coverage is therefore a property of HOW THE DESTINATION WAS SPELLED AT THE CALL SITE, not of the value crossing it and not of the declaration identity the spelling resolves to. The subject is every structural-text boundary in the corpus, because any of them can be relocated behind one line an author can write without touching the value, the callee, or the guarded declaration — and that set is unbounded because it is authored on demand rather than enumerable from the current tree. A census of exact-spelling boundaries is therefore a LOWER BOUND on the subject and must not be read as its size.\n\nCONSEQUENCE FOR THE TRIGGER, which is why this correction is load-bearing rather than a widening for its own sake: the restoration trigger above names the peeling capability and its A/B is stated over fixtures at the v2.std.text.String spelling. Every one of those fixtures can be satisfied while an aliased boundary of the same class still admits, so the trigger as written would retire this row with the hole open — the grain mismatch DESIGN 4b(3) names, a corpus loss against a spelling-shaped trigger. The trigger is therefore RESTATED to require the capability to survive aliasing: the A/B must include a boundary declared through a local alias whose target is the guarded declaration, and that arm must REFUSE alongside the unaliased one, with the grounded-identity controls still ADMITTED. A capability that refuses only the exact spelling is not the capability this row is waiting for. AND IT MUST SURVIVE CALLER RE-RESOLUTION TOO, which is the same grain mismatch one level out: the A/B must also include a boundary whose callee declares its parameter with a bare spelling that is structural IN THE DECLARING MODULE, called from a foreign module, and that arm must refuse as well. A trigger satisfied by aliasing but not by caller re-resolution would retire this row against one of its two escapes.\n\nAND THE SCOPE AXIS, WHICH IS A SECOND WAY THE SAME BOUNDARY GETS TWO MEANINGS AND IS NOT THE ALIASING ONE. A CALLEE PARAMETER TYPE IS RE-RESOLVED IN THE CALLER ENVIRONMENT, so one declaration presents different types to its own module and to a foreign caller. Measured by session bold-carp-449, both arms forcing the refusal with an Int actual so the diagnostic NAMES the destination and no peel-admission can be mistaken for agreement: inside a module declaring type String = FreeMonoid, a call to its own fn taking s: String refuses with expected Node(String), so the module DOES get its own spelling and locals do beat kernel in-module; from a foreign module with no import and no alias, a call to v2.std.text string_head refuses with expected Primitive(String). Same declaration, same parameter, two destination types, decided by whose scope resolved the signature.\n\nSO THE COVERAGE IS A PROPERTY OF HOW THE DESTINATION WAS SPELLED AND OF WHICH SCOPE RESOLVED IT. A wall keyed on the guarded declaration can be defeated by a CALLER without touching the destination at all, which is a strictly different escape than the aliasing one above: aliasing rewrites the destination, this rewrites nothing and still changes what the destination means. Every operation in v2.std.text whose parameter is spelled with the bare String -- string_head, string_tail, string_is_empty and their siblings -- is therefore structural to its own module and HOST to every foreign call site, which is the mechanical cause of the live specimen in src/v2/std/integer.dag: it calls string_head, the caller-side reading types the parameter as the kernel String, and the emitted Rust then calls a function whose real parameter is the structural carrier. One declaration, two meanings, with the emitter following the caller-side reading. That is a section 3 meaning fork with a mechanical cause rather than a spelling accident.\n\nTWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the re-resolution applies to RETURN types as well as parameters, and whether writing the QUALIFIED spelling in the callee declaration itself is immune to it. If it is immune, the repair for that module is one line per declaration and is worth establishing before anything larger is designed.\n\nCORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's.\n\nCLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT this authority: it keeps a provenance-keyed answer, a second authority for the same fact, declared as its own typed stall row gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall with its population and trigger. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here.\n\nSTANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. The identity each row reports as unread is v1's ONE type-reference derivation, `v1.compiler.infer_env` `type_reference_declaration_reading` (its `TypeReferenceUnidentified` arm), the same read `v1.tests.claim.carrier_realization_census` records per authored reference position; the census is NOT this population's producer, because it enumerates authored type-reference positions inside declarations, while this population is checker comparisons at use sites whose value side is usually an inferred type with no authored position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text." } } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index a42c68758d8..2448cdf988a 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -108,7 +108,9 @@ Producer: `tools.emission_entry_instrument::measure_entry_emission` ### Non-literal kernel-String refusal at the structural text boundary — declared 2026-08-30 -**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. +**CURRENT STANDING (2026-09-29, after the CLIMB below): a text crossing between two IDENTIFIED sides is structurally guaranteed -- a non-literal kernel-String value at an identified code-point-sequence boundary unfolds through the declared row or refuses, located. This row stands for exactly two remaining populations, each with its own trigger stated below: (1) CALLABLE-SIGNATURE crossings -- a callable whose parameters are code-point sequences passed where a callable over host text is expected, which callable-signature agreement does not yet judge by the text classifier; (2) the UNIDENTIFIED population -- a boundary where one side has no readable declaration identity, which the wall declines to judge (STANDING RESTATED, at the end). The Rust renderer's separate text-carrier answer is its own row, gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall.** + +HISTORY, AS DECLARED 2026-08-30 (the structural-text landing), superseded by the CLIMB below -- kept as history, not as standing: **A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DID NOT REFUSE.** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINED ADMITTED at that boundary (until the CLIMB) and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. POPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses. @@ -124,7 +126,7 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. -CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT yet this authority: its renderers hold no TypeEnv, so it keeps its provenance-keyed answer, measured to render the self-host crate byte-identically to base, and unifying the two waits on the render paths carrying the env. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT this authority: it keeps a provenance-keyed answer, a second authority for the same fact, declared as its own typed stall row gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall with its population and trigger. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. STANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. The identity each row reports as unread is v1's ONE type-reference derivation, `v1.compiler.infer_env` `type_reference_declaration_reading` (its `TypeReferenceUnidentified` arm), the same read `v1.tests.claim.carrier_realization_census` records per authored reference position; the census is NOT this population's producer, because it enumerates authored type-reference positions inside declarations, while this population is checker comparisons at use sites whose value side is usually an inferred type with no authored position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text. From fa531596c4550994bd38f5661192aafed2c5bd2b Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 01:27:41 +0000 Subject: [PATCH 14/24] module_graph imports ends_with explicitly (floor UnimportedBareProvider) This PR touches src/v2/lens/module_graph.dag, so the floor's changed-witness gate now reads it. Its pre-existing bare ends_with read is refused because the file declares imports. Import it from gunbc.rust_item_scan, the declaration the bare read binds to, as #12494 did for the same refusal. Entry compile: 0 blocking. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/lens/module_graph.dag | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v2/lens/module_graph.dag b/src/v2/lens/module_graph.dag index cb10ad77891..53035f31e0e 100644 --- a/src/v2/lens/module_graph.dag +++ b/src/v2/lens/module_graph.dag @@ -33,6 +33,7 @@ import v2.std.diagnostic { outcome_rejected } import v2.std.logic { Bool } +import gunbc.rust_item_scan { ends_with } import v2.std.node { Node, Symbol, symbol_eq } import v2.std.qualified_name { QualifiedName, From 65fad5b09cbbd7d6f0fcb520761d4396d0c9b0d7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 01:28:22 +0000 Subject: [PATCH 15/24] Revert the gunbc.rust_item_scan ends_with import into v2.lens (layer inversion) A gunbc tool must not provide names to v2.lens (wise-dove-693; the same refusal was ruled on #12526). The agreed fix is clever-heron-784's PR deleting rust_item_scan's duplicate ends_with, so that the bare read binds the host primitive. #12512 lands after it. This reverts commit fa531596c4550994bd38f5661192aafed2c5bd2b. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/lens/module_graph.dag | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v2/lens/module_graph.dag b/src/v2/lens/module_graph.dag index 53035f31e0e..cb10ad77891 100644 --- a/src/v2/lens/module_graph.dag +++ b/src/v2/lens/module_graph.dag @@ -33,7 +33,6 @@ import v2.std.diagnostic { outcome_rejected } import v2.std.logic { Bool } -import gunbc.rust_item_scan { ends_with } import v2.std.node { Node, Symbol, symbol_eq } import v2.std.qualified_name { QualifiedName, From 178804ca74d54952613954cfafaf11e1e266f165 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 03:20:16 +0000 Subject: [PATCH 16/24] One text crossing rule; wall comment states what it executes (review 73008) - v1.compiler.infer_env text_representations_cross is the one TextRepresentation x TextRepresentation crossing rule; text_crossing_by_identity and the call-argument check both use it (04_infer text_representations_disagree deleted). - 04_types comment no longer claims the wall refuses an unidentified side: base decides it, text_unjudged_advisories counts it, gunbc.rung_drop text_boundary_identity_wall bounds it. - argument_text_representation drops its dead module_name parameter and the stale two-view prose. Behaviour-identical to H: stage0 fixed point; self-host emit 0 files differ; the six wall/neighbour claim files identical (102 PASS); text_boundary_identity_wall_witness_test 21/21, same names. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/04_env.dag | 14 ++-- src/v1/04_infer.dag | 32 ++------ src/v1/04_types.dag | 6 +- src/v1/stage0/src/v1_compiler_infer.rs | 91 ++++++++-------------- src/v1/stage0/src/v1_compiler_infer_env.rs | 37 +++++---- 5 files changed, 74 insertions(+), 106 deletions(-) diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index 2c22e87324b..f6db58535b0 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -1859,11 +1859,15 @@ fn text_representation_is_text_arm(r: TextRepresentation) -> Bool { // (gunbc.rung_drop text_boundary_identity_wall) whose trigger is the resolver recording declaration // identity on every type reference. fn text_crossing_by_identity(left: Node, right: Node, env: TypeEnv) -> Bool { - let l = text_representation_by_identity(n: left, env: env) - let r = text_representation_by_identity(n: right, env: env) - match l { - HostText => match r { CodePointSequence => true TextRepresentationUnidentified => false HostText => false NotText => false } - CodePointSequence => match r { HostText => true TextRepresentationUnidentified => false CodePointSequence => false NotText => false } + text_representations_cross(a: text_representation_by_identity(n: left, env: env), b: text_representation_by_identity(n: right, env: env)) +} + +// THE ONE CROSSING RULE: host text against a code-point sequence, in either order. Every other pair, +// including one with an unidentified side, is not a crossing. +fn text_representations_cross(a: TextRepresentation, b: TextRepresentation) -> Bool { + match a { + HostText => match b { CodePointSequence => true TextRepresentationUnidentified => false HostText => false NotText => false } + CodePointSequence => match b { HostText => true TextRepresentationUnidentified => false CodePointSequence => false NotText => false } NotText => false TextRepresentationUnidentified => false } diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index 2004e8b4d01..d73d4fddbf9 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -119,7 +119,7 @@ import v1.compiler.infer_method { import v1.compiler.infer_cycle { detect_type_cycles_kahn } import v1.compiler.coercion { provenance_realizes_natively } import v1.compiler.infer_env { - text_representation_by_identity, text_crossing_by_identity, text_representation_is_text_arm, text_representation_is_unidentified, + text_representation_by_identity, text_crossing_by_identity, text_representations_cross, text_representation_is_text_arm, text_representation_is_unidentified, TypeEnv, TypeBinding, TypeEnvCache, empty_type_env_cache, merge_type_env_cache, merge_type_env_cache_guarded, GuardedTypeEnvCacheMerge, TypeEnvCacheMergeConflict, str_bindings_from_bindings, @@ -6083,17 +6083,8 @@ data direct_call_shape_wall_note: String = "WALL (DESIGN §5) — the compile se // A VALUE'S TEXT REPRESENTATION IS ITS RESOLVED TYPE'S DECLARATION IDENTITY // (v1.compiler.infer_env text_representation_by_identity). It is NOT re-read through // peel_nominal_alias_identity: that peel looks the spelling up by name in THIS scope, which is the -// caller-re-resolution escape. The history below records why an earlier revision read two views. -// A VALUE'S TEXT REPRESENTATION WAS READ FROM BOTH OF ITS TYPE VIEWS. The resolved type and its -// alias-peeled form each lose the structure in one known case: a qualified v2.std.text.String value's -// resolved node still reads as the spelling String (so only the peel sees the carrier), while a node -// typed by its declaring module's alias spelling String is re-resolved by name in the caller's -// environment by the peel (so only the resolved view sees it). Either view establishing a code-point -// sequence establishes it; host text only when neither view does -- EXCEPT that a resolved type the -// kernel minted is host text outright: the peel looks its spelling up by name, and a by-name lookup -// of the kernel spelling String may find a corpus declaration, which kernel precedence says it never -// binds to. -fn argument_text_representation(value: Node, type_env: TypeEnv, module_name: String) -> TextRepresentation { +// caller-re-resolution escape. +fn argument_text_representation(value: Node, type_env: TypeEnv) -> TextRepresentation { text_representation_by_identity(n: expression_value_type(e: value), env: type_env) } @@ -6120,15 +6111,6 @@ fn text_representations_both_code_point(a: TextRepresentation, b: TextRepresenta } } -fn text_representations_disagree(a: TextRepresentation, b: TextRepresentation) -> Bool { - match a { - NotText => false - HostText => match b { CodePointSequence => true TextRepresentationUnidentified => false HostText => false NotText => false } - CodePointSequence => match b { HostText => true TextRepresentationUnidentified => false CodePointSequence => false NotText => false } - TextRepresentationUnidentified => false - } -} - fn direct_call_arg_mismatch_diags( plan: List, typed_args: List, @@ -6153,9 +6135,9 @@ fn direct_call_arg_mismatch_diags( let actual_raw = expression_value_type(e: actual_expr) let actual = peel_nominal_alias_identity(n: actual_raw, env: type_env, module_name: module_name) let formal_text = if formal_code_point_view(formal: app.formal, env: type_env) != none { CodePointSequence } else { text_representation_by_identity(n: formal, env: type_env) } - let actual_text = argument_text_representation(value: actual_expr, type_env: type_env, module_name: module_name) + let actual_text = argument_text_representation(value: actual_expr, type_env: type_env) let unjudged = text_unjudged_advisories(a: formal_text, b: actual_text, position: "a call argument", span: actual_expr.span, module_name: module_name) - concat(unjudged, if text_representations_disagree(a: formal_text, b: actual_text) { + concat(unjudged, if text_representations_cross(a: formal_text, b: actual_text) { [make_error_node(diagnostic: InternalError { message: concat("text representation crossing at a call argument: declared ", node_type_shape(n: formal, source_indices: source_indices), ", produced ", node_type_shape(n: actual, source_indices: source_indices), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text"), span: actual_expr.span }, module_name: module_name)] } else if text_representations_both_code_point(a: formal_text, b: actual_text) { [] @@ -6232,7 +6214,7 @@ fn builtin_text_argument_crossing_diags(func_name: String, typed_args: List - match argument_text_representation(value: value, type_env: scope.type_env, module_name: scope.module_name) { + match argument_text_representation(value: value, type_env: scope.type_env) { CodePointSequence => [inference_error( message: concat("text representation crossing: argument '", bound_name, "' of ", func_name, " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence"), @@ -7206,7 +7188,7 @@ fn destination_declares_scalar_sequence_unfold(destination_type: Node, scope: In fn host_text_unfolded_at_code_point_boundary(value: Node, destination_type: Node, scope: InferScope) -> Node { match value.inferred { Present { value: Resolved { node: _ } } => - match argument_text_representation(value: value, type_env: scope.type_env, module_name: scope.module_name) { + match argument_text_representation(value: value, type_env: scope.type_env) { HostText => if destination_declares_scalar_sequence_unfold(destination_type: destination_type, scope: scope) { make_named_expr_node(occurrence_identity: OccurrenceSynthetic, diff --git a/src/v1/04_types.dag b/src/v1/04_types.dag index 7d2366c6b57..b9e8a912ba2 100644 --- a/src/v1/04_types.dag +++ b/src/v1/04_types.dag @@ -1078,8 +1078,10 @@ fn node_type_equals(left: Node, right: Node, source_indices: Map String { CACHED.with(|c: &String| c.clone()) } -pub fn argument_text_representation( - value: Rc, - type_env: Rc, - module_name: String, -) -> TextRepresentation { +pub fn argument_text_representation(value: Rc, type_env: Rc) -> TextRepresentation { crate::v1_compiler_infer_env::text_representation_by_identity( expression_value_type(value.clone()), type_env.clone(), @@ -8872,25 +8868,6 @@ pub fn text_representations_both_code_point(a: TextRepresentation, b: TextRepres } } -pub fn text_representations_disagree(a: TextRepresentation, b: TextRepresentation) -> bool { - match a.clone() { - TextRepresentation::NotText => false, - TextRepresentation::HostText => match b.clone() { - TextRepresentation::CodePointSequence => true, - TextRepresentation::TextRepresentationUnidentified => false, - TextRepresentation::HostText => false, - TextRepresentation::NotText => false, - }, - TextRepresentation::CodePointSequence => match b.clone() { - TextRepresentation::HostText => true, - TextRepresentation::TextRepresentationUnidentified => false, - TextRepresentation::CodePointSequence => false, - TextRepresentation::NotText => false, - }, - TextRepresentation::TextRepresentationUnidentified => false, - } -} - pub fn direct_call_arg_mismatch_diags( plan: Rc>>, typed_args: Rc>>, @@ -8922,9 +8899,9 @@ let formal_text = if (formal_code_point_view(app.formal.clone(), type_env.clone( } else { crate::v1_compiler_infer_env::text_representation_by_identity(formal.clone(), type_env.clone()) }; -let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone(), module_name.clone()); +let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone()); let unjudged = text_unjudged_advisories(formal_text.clone(), actual_text.clone(), "a call argument".to_string(), actual_expr.span.clone(), module_name.clone()); -v1_rt::concat(unjudged.clone(), if text_representations_disagree(formal_text.clone(), actual_text.clone()) { +v1_rt::concat(unjudged.clone(), if crate::v1_compiler_infer_env::text_representations_cross(formal_text.clone(), actual_text.clone()) { Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::InternalError { message: v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing at a call argument: declared ".to_string(), crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone())), ", produced ".to_string()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone())), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text".to_string()), span: actual_expr.span.clone(), @@ -9003,7 +8980,7 @@ if !{ let mut __found = false; for n in host_params.iter().cloned() { if (n.clon { let value = crate::v1_std_core::arg_value(arg.clone()); match value.inferred.clone().as_deref().cloned() { - Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation(value.clone(), scope.type_env.clone(), scope.module_name.clone()) { + Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation(value.clone(), scope.type_env.clone()) { TextRepresentation::CodePointSequence => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing: argument '".to_string(), bound_name.clone()), "' of ".to_string()), func_name.clone()), " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence".to_string()), value.span.clone(), scope.module_name.clone())]), TextRepresentation::HostText => Rc::new(vec![]), TextRepresentation::NotText => Rc::new(vec![]), @@ -10960,37 +10937,37 @@ pub fn host_text_unfolded_at_code_point_boundary( scope: Rc, ) -> Rc { match value.inferred.clone().as_deref().cloned() { - Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation( - value.clone(), - scope.type_env.clone(), - scope.module_name.clone(), - ) { - TextRepresentation::HostText => { - if destination_declares_scalar_sequence_unfold( - destination_type.clone(), - scope.clone(), - ) { - crate::v1_std_core::make_named_expr_node( - Rc::new(NodeOccurrenceIdentity::OccurrenceSynthetic), - "chars".to_string(), - Rc::new(ExprData::ExprMethodCall { - method_semantics: Some(Rc::new(MethodSemantics::PlainMethodSemantics)), - }), - Rc::new(vec![value.clone()]), - Some(Rc::new(InferredNode::Resolved { - node: destination_type.clone(), - })), - value.span.clone(), - elaborated_span("chars".to_string()), - ) - } else { - value.clone() + Some(InferredNode::Resolved { node: _, .. }) => { + match argument_text_representation(value.clone(), scope.type_env.clone()) { + TextRepresentation::HostText => { + if destination_declares_scalar_sequence_unfold( + destination_type.clone(), + scope.clone(), + ) { + crate::v1_std_core::make_named_expr_node( + Rc::new(NodeOccurrenceIdentity::OccurrenceSynthetic), + "chars".to_string(), + Rc::new(ExprData::ExprMethodCall { + method_semantics: Some(Rc::new( + MethodSemantics::PlainMethodSemantics, + )), + }), + Rc::new(vec![value.clone()]), + Some(Rc::new(InferredNode::Resolved { + node: destination_type.clone(), + })), + value.span.clone(), + elaborated_span("chars".to_string()), + ) + } else { + value.clone() + } } + TextRepresentation::CodePointSequence => value.clone(), + TextRepresentation::NotText => value.clone(), + TextRepresentation::TextRepresentationUnidentified => value.clone(), } - TextRepresentation::CodePointSequence => value.clone(), - TextRepresentation::NotText => value.clone(), - TextRepresentation::TextRepresentationUnidentified => value.clone(), - }, + } _ => value.clone(), } } diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index e5ce1dae3bc..aa3339d6ea2 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -3188,25 +3188,28 @@ pub fn text_representation_is_text_arm(r: TextRepresentation) -> bool { } pub fn text_crossing_by_identity(left: Rc, right: Rc, env: Rc) -> bool { - { - let l = text_representation_by_identity(left.clone(), env.clone()); - let r = text_representation_by_identity(right.clone(), env.clone()); - match l.clone() { - TextRepresentation::HostText => match r.clone() { - TextRepresentation::CodePointSequence => true, - TextRepresentation::TextRepresentationUnidentified => false, - TextRepresentation::HostText => false, - TextRepresentation::NotText => false, - }, - TextRepresentation::CodePointSequence => match r.clone() { - TextRepresentation::HostText => true, - TextRepresentation::TextRepresentationUnidentified => false, - TextRepresentation::CodePointSequence => false, - TextRepresentation::NotText => false, - }, + text_representations_cross( + text_representation_by_identity(left.clone(), env.clone()), + text_representation_by_identity(right.clone(), env.clone()), + ) +} + +pub fn text_representations_cross(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::HostText => match b.clone() { + TextRepresentation::CodePointSequence => true, + TextRepresentation::TextRepresentationUnidentified => false, + TextRepresentation::HostText => false, TextRepresentation::NotText => false, + }, + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::HostText => true, TextRepresentation::TextRepresentationUnidentified => false, - } + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, } } From 423ee76ead304b043db40e0a8b40f7824c6c388e Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 19:42:44 +0000 Subject: [PATCH 17/24] Declare the FreeSemigroup text crossing's second decider as a stall gunbc.guarantee_stall free_semigroup_text_crossing_decided_by_spelling_stall: at FreeMonoid the identity wall decides host-text crossings; at FreeSemigroup the classifier reads NotText, so the spelling-keyed kernel_value_declared_type_mismatch (extended by #12695) decides. Both agree on every fixture (wall 21/21, kernel_refinement_at_structured_parameter 8/8 on one binary). The trigger names the capability: the classifier recognizes FreeSemigroup, so every such crossing is decided by text_representations_cross and the base check's text case retires. guarantee_stall_witness_test 11/11. Co-Authored-By: Claude Opus 5.5 (1M context) --- ...ext_crossing_decided_by_spelling_stall.dag | 19 +++++++++++++++++++ dag/gunbc/guarantee_stall/roster.dag | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 dag/gunbc/guarantee_stall/free_semigroup_text_crossing_decided_by_spelling_stall.dag diff --git a/dag/gunbc/guarantee_stall/free_semigroup_text_crossing_decided_by_spelling_stall.dag b/dag/gunbc/guarantee_stall/free_semigroup_text_crossing_decided_by_spelling_stall.dag new file mode 100644 index 00000000000..409bfeaeab8 --- /dev/null +++ b/dag/gunbc/guarantee_stall/free_semigroup_text_crossing_decided_by_spelling_stall.dag @@ -0,0 +1,19 @@ +module gunbc.guarantee_stall.free_semigroup_text_crossing_decided_by_spelling_stall + +import gunbc.guarantee_rung { StructurallyGuaranteed, StructurallyImpossible } +import gunbc.guarantee_stall { GuaranteeStall, ClimbableButUnbuilt, BoundedPopulation, climbs_when } + +data free_semigroup_text_crossing_decided_by_spelling_stall: GuaranteeStall = GuaranteeStall { + subject: "Two deciders answer whether host text may cross into a structural text formal. At a FreeMonoid formal the declaration-identity wall decides (v1.compiler.infer_env text_representations_cross; gunbc.rung_drop text_boundary_identity_wall): the value unfolds through the declared row or the crossing refuses, before the base check runs, and the base check then sees an argument already typed as the destination. At a FreeSemigroup formal -- a nonempty code-point sequence, the shape v2.std.text string_replace takes as its needle -- the identity classifier reads the formal as NotText, so the wall makes no judgment and the pre-existing, spelling-keyed base inhabitance check decides (v1.compiler.infer kernel_value_declared_type_mismatch, extended by gunbc#12695 to peel the produced side's where-refinement). The two agree on every fixture (text_boundary_identity_wall_witness_test 21/21 and kernel_refinement_at_structured_parameter_witness_test 8/8 on one binary), but they are two authorities for one question, and the second keys on spelling rather than identity. Nothing fell -- the base check decided this case before the wall existed -- so this is a stall, not a drop.", + current: StructurallyGuaranteed, + ceiling: StructurallyImpossible, + blocker: ClimbableButUnbuilt, + population: BoundedPopulation { members: [ + "v1.compiler.infer_env.text_representation_by_identity", + "v1.compiler.infer_env.text_representations_cross", + "v1.compiler.infer.kernel_value_declared_type_mismatch", + "test.claim.kernel_refinement_at_structured_parameter_witness_test.a_cast_nonempty_needle_at_string_replace_must_refuse", + "test.claim.kernel_refinement_at_structured_parameter_witness_test.a_plain_literal_needle_at_string_replace_still_refuses" + ] }, + next_rung_trigger: climbs_when(capability: "the identity classifier recognizes std.algebra FreeSemigroup applied to std.types Char as a (nonempty) code-point sequence, SUFFICIENT FOR every host-text crossing into a structural text formal -- FreeMonoid and FreeSemigroup alike -- to be decided by text_representations_cross, with kernel_value_declared_type_mismatch's text case retired (no text crossing reaches it), and kernel_refinement_at_structured_parameter_witness_test still passing through the identity route.") +} diff --git a/dag/gunbc/guarantee_stall/roster.dag b/dag/gunbc/guarantee_stall/roster.dag index 1f3ffb51e93..47a5dfb9daf 100644 --- a/dag/gunbc/guarantee_stall/roster.dag +++ b/dag/gunbc/guarantee_stall/roster.dag @@ -70,6 +70,7 @@ import gunbc.guarantee_stall.module_cited_only_in_annotation_prose_stall { modul import gunbc.guarantee_stall.joint_claim_join_private_corpus_unindexed_stall { joint_claim_join_private_corpus_unindexed_stall } import gunbc.guarantee_stall.coercion_two_algebras_answer_one_question_stall { coercion_two_algebras_answer_one_question_stall } import gunbc.guarantee_stall.text_carrier_render_not_keyed_on_identity_stall { text_carrier_render_not_keyed_on_identity_stall } +import gunbc.guarantee_stall.free_semigroup_text_crossing_decided_by_spelling_stall { free_semigroup_text_crossing_decided_by_spelling_stall } import gunbc.guarantee_stall.emitted_inline_crate_path_edges_unreturned_stall { emitted_inline_crate_path_edges_unreturned_stall } // THE COHORT PROVENANCE NOTES BELOW WERE AUTHORED WHEN EVERY ROW SAT IN ONE FILE, and they are @@ -236,6 +237,7 @@ data all_guarantee_stalls: List = [ fabric_purpose_handover_registration_unobserved_stall, coercion_two_algebras_answer_one_question_stall, text_carrier_render_not_keyed_on_identity_stall, + free_semigroup_text_crossing_decided_by_spelling_stall, emitted_inline_crate_path_edges_unreturned_stall, ] From 6beefd2566d519035cf3004401f9ec250da562b7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 1 Oct 2026 12:51:36 +0000 Subject: [PATCH 18/24] Restore the substrate-vocabulary skip: it is not redundant The previous commit deleted cli_run.rs claim_scope_for_with_memos' is_substrate_vocabulary skip on the strength of bare_name_ambiguity_wall_witness_test passing 10/10 without it. That fixture file is covered by main's exit-arm parse fix, but real corpus consumers are not: with the skip removed, the required floor refused gunbc.output_policy (AmbiguousBareNameRead, String, 4 sites: extdeps.cloudflare.account_api_tokens, extdeps.docker.container_inspect, extdeps.github.issues, extdeps.github.pulls). The skip is restored, and its comment records why it stays. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/stage0/src/cli_run.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 0b6d226eaed..2ef3df4c346 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -43850,6 +43850,17 @@ fn claim_scope_for_with_memos( }; let site_file = module.module.span.file.as_str(); for name in refs.iter() { + // A kernel or container spelling binds the substrate, never a declaring module + // (`is_substrate_vocabulary`, the one rule every bare-name producer reads), so two + // corpus modules declaring `String` do not make a bare `String` contested. This + // held by accident while some import in each scope named one of the declarers; + // gunbc.rung_drop text_boundary_identity_wall deleted 71 such imports. Not redundant + // with the parse fix that carries a service exit arm's type as a type: bare `String` + // reads outside exit arms remain (measured: the floor refused gunbc.output_policy at + // 4 sites, e.g. extdeps.github.issues, with this skip removed). + if is_substrate_vocabulary(name) { + continue; + } let Some(claimants) = ambiguous.get(name) else { continue; }; From 9153d4a4d4a17ca158707675dd488cae93ec7090 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 2 Oct 2026 07:30:58 +0000 Subject: [PATCH 19/24] =?UTF-8?q?Regenerate=20stage0=20infer=20mirrors=20t?= =?UTF-8?q?o=20the=20fixed=20point=20(claim=5Fexecutor=20--required-regen,?= =?UTF-8?q?=20gunbc=20sha256=209c31f4a6=E2=80=A6,=20rounds=201=3D=3D2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/stage0/src/v1_compiler_infer.rs | 589 +++++++++++++++--- src/v1/stage0/src/v1_compiler_infer_env.rs | 390 +++++++++++- src/v1/stage0/src/v1_compiler_infer_method.rs | 62 +- 3 files changed, 937 insertions(+), 104 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 7ae45e5660b..b0c5f4c9c2a 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -19,11 +19,16 @@ use crate::std_algebra::CollectionSizeEffect::ShrinkEffect; pub use crate::std_algebra::{carrier_container_equality_rows, kernel_carrier_admits_numeral}; pub use crate::std_algebra::{CollectionSizeEffect, FreeMonoid}; use crate::std_coercion::SubtractionRefinement::SubtractionRefinementAmbiguous; +use crate::std_coercion::TextRepresentation::{ + CodePointSequence, HostText, NotText, TextRepresentationUnidentified, +}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; pub use crate::std_coercion::{dag_can_cast, dag_cast_requires_proof, is_dag_cast_domain_type}; -pub use crate::std_coercion::{SubtractionRefinement, TypeDeclarationProvenance}; +pub use crate::std_coercion::{ + SubtractionRefinement, TextRepresentation, TypeDeclarationProvenance, +}; pub use crate::std_computation::ShrinkFactor; use crate::std_computation::ShrinkFactor::{ConstantShrink, ProportionalShrink, UnitShrink}; use crate::std_content_hash::ContentHash::*; @@ -116,7 +121,9 @@ pub use crate::v1_compiler_infer_env::{ qualified_all_but_last, qualify_borrowed_inferred, qualify_borrowed_type_names, qualify_decl_reference_positions, str_bindings_from_bindings, symbol_index_insert, symbol_index_insert_decl, symbol_index_insert_service, symbol_index_lookup, - type_reference_declaration, type_reference_declaration_ref, unit_variant_index_shadow_insert, + text_crossing_by_identity, text_representation_by_identity, text_representation_is_text_arm, + text_representation_is_unidentified, text_representations_cross, type_reference_declaration, + type_reference_declaration_ref, unit_variant_index_shadow_insert, }; pub use crate::v1_compiler_infer_env::{ GlobalBareCandidate, GlobalBareLookupState, GuardedTypeEnvCacheMerge, ServiceCensusEntry, @@ -157,7 +164,8 @@ pub use crate::v1_compiler_infer_lookup::{ ExactDeclarationIdentity, KnownMethodResolution, }; pub use crate::v1_compiler_infer_method::{ - builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, + builtin_host_text_param_names, builtin_kernel_seed_diagnostics, builtin_param_names, + infer_builtin_call_type, resolve_builtin_call_type, }; use crate::v1_compiler_infer_patterns::PatternSubject::*; pub use crate::v1_compiler_infer_patterns::{ @@ -206,7 +214,10 @@ pub use crate::v1_compiler_infer_sigs::{ CallableCandidate, CallableIdentity, DerivedCalleeSig, FuncSigLookup, NoDerivableSigReason, ResolveFuncSigsResult, ResolvedFormals, ResolvedFuncEnv, ResolvedFuncSig, }; -pub use crate::v1_compiler_infer_types::KernelTypeBuild; +use crate::v1_compiler_infer_types::TextJudgment::{TextJudgedIn, TextNotAsked}; +use crate::v1_compiler_infer_types::TextNotAskedReason::{ + TextNotAskedForCallableComponentResidue, TextNotAskedInVariantFieldSummary, +}; pub use crate::v1_compiler_infer_types::{ bare_map_node, bare_set_node, callable_inferred, callable_return_type, child_type_node, emit_map_has, extract_optional_inner_node, for_each_element_type_node, infer_binop_type_node, @@ -219,6 +230,7 @@ pub use crate::v1_compiler_infer_types::{ structural_carrier_template_name, subtraction_refinement_of, template_return_has_variables, template_return_is_receiver_self, }; +pub use crate::v1_compiler_infer_types::{KernelTypeBuild, TextJudgment, TextNotAskedReason}; pub use crate::v1_compiler_resolve::{ModuleGraph, ResolvedImport, ResolvedModule}; use crate::v1_compiler_type_head_exposure::TypeHeadExposure::{ ExposedTypeHead, MalformedApplicationHead, OpaqueTypeHead, StuckTypeHead, @@ -250,9 +262,9 @@ use crate::v1_std_core::CompilerDiagnostic::{ FrontierOccurrenceBudgetExceeded, InternalError, MethodExistenceFrontierAdmitted, MethodExistenceUndecided, MethodNotFound, MissingField, OptionalCastNotEliminated, ReceiverTypeUnestablished, ServiceConfigReferenceJudgmentDeferred, - SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, TypeArgumentArityMismatch, - TypeMismatch, TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, - VariantCollision, + SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, + TextRepresentationUnidentifiedAtBoundary, TypeArgumentArityMismatch, TypeMismatch, + TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, VariantCollision, }; use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::DeclarationMarker::Unmarked; @@ -2080,6 +2092,41 @@ pub fn declared_type_conformance_diags( produced_by: Rc, span: Rc, scope: Rc, +) -> Rc>> { + v1_rt::concat( + text_unjudged_advisories( + crate::v1_compiler_infer_env::text_representation_by_identity( + declared.clone(), + scope.type_env.clone(), + ), + crate::v1_compiler_infer_env::text_representation_by_identity( + produced.clone(), + scope.type_env.clone(), + ), + "a declared type".to_string(), + span.clone(), + scope.module_name.clone(), + ), + declared_type_conformance_diags_core( + position.clone(), + subject.clone(), + declared.clone(), + produced.clone(), + produced_by.clone(), + span.clone(), + scope.clone(), + ), + ) +} + +pub fn declared_type_conformance_diags_core( + position: DeclaredTypePosition, + subject: String, + declared: Rc, + produced: Rc, + produced_by: Rc, + span: Rc, + scope: Rc, ) -> Rc>> { { let si = scope.type_env.clone().source_indices.clone(); @@ -2099,10 +2146,10 @@ pub fn declared_type_conformance_diags( if ((cardinality_diags.clone().len() as i64) > 0) { cardinality_diags.clone() } else { - if declared_type_kernel_inhabitance_mismatch_here_or_at_element( + if crate::v1_compiler_infer_env::text_crossing_by_identity( declared.clone(), produced.clone(), - scope.clone(), + scope.type_env.clone(), ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape(declared.clone(), si.clone()), @@ -2111,12 +2158,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if (nominal_product_inhabitance_refusal( + if declared_type_kernel_inhabitance_mismatch_here_or_at_element( declared.clone(), produced.clone(), scope.clone(), - ) != std::option::Option::None) - { + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2130,11 +2176,12 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if nominal_coproduct_applied_argument_conflict( + if (nominal_product_inhabitance_refusal( declared.clone(), produced.clone(), scope.clone(), - ) { + ) != std::option::Option::None) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2148,7 +2195,7 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if coproduct_payload_where_parent_required( + if nominal_coproduct_applied_argument_conflict( declared.clone(), produced.clone(), scope.clone(), @@ -2166,14 +2213,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if ((type_node_is_arrow(declared.clone()) - && type_node_is_arrow(produced.clone())) - && callable_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - )) - { + if coproduct_payload_where_parent_required( + declared.clone(), + produced.clone(), + scope.clone(), + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2187,11 +2231,14 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if callable_element_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if ((type_node_is_arrow(declared.clone()) + && type_node_is_arrow(produced.clone())) + && callable_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + )) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2205,28 +2252,50 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if !both_ground.clone() { - Rc::new(vec![]) + if callable_element_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + ) { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) } else { - if crate::v1_compiler_infer_types::node_type_compatible( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if !both_ground.clone() { Rc::new(vec![]) } else { - Rc::new(vec![type_mismatch_error( - crate::v1_compiler_infer_types::node_type_shape( - declared.clone(), - si.clone(), - ), - crate::v1_compiler_infer_types::node_type_shape( - produced.clone(), - si.clone(), - ), - span.clone(), - scope.module_name.clone(), - )]) + if crate::v1_compiler_infer_types::node_type_compatible( + declared.clone(), + produced.clone(), + si.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), + ) { + Rc::new(vec![]) + } else { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) + } } } } @@ -2812,6 +2881,7 @@ pub fn set_element_types_mismatch( recv_elem: Rc, operand_elem: Rc, source_indices: Rc>>, + text: Rc, ) -> bool { ((set_element_type_is_concrete(recv_elem.clone()) && set_element_type_is_concrete(operand_elem.clone())) @@ -2819,6 +2889,7 @@ pub fn set_element_types_mismatch( recv_elem.clone(), operand_elem.clone(), source_indices.clone(), + text.clone(), )) } @@ -3972,6 +4043,9 @@ pub fn match_arm_types_are_proven_disjoint( unified_arm_type.clone(), arm_type.clone(), source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) == false)) } } @@ -8160,6 +8234,9 @@ pub fn callable_component_ground_mismatch( formal_part.clone(), instantiated.clone(), source_indices.clone(), + Rc::new(TextJudgment::TextNotAsked { + reason: TextNotAskedReason::TextNotAskedForCallableComponentResidue, + }), ) } else { false @@ -8421,12 +8498,16 @@ pub fn direct_call_arg_type_mismatch( { false } else { - ((nominal_call_arg_brand_mismatch( + (((crate::v1_compiler_infer_env::text_crossing_by_identity( + formal.clone(), + actual.clone(), + type_env.clone(), + ) || nominal_call_arg_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), source_indices.clone(), - ) || container_element_nominal_brand_mismatch( + )) || container_element_nominal_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), @@ -9097,6 +9178,49 @@ pub fn direct_call_shape_wall_note() -> String { CACHED.with(|c: &String| c.clone()) } +pub fn argument_text_representation(value: Rc, type_env: Rc) -> TextRepresentation { + crate::v1_compiler_infer_env::text_representation_by_identity( + expression_value_type(value.clone()), + type_env.clone(), + ) +} + +pub fn text_unjudged_advisories( + a: TextRepresentation, + b: TextRepresentation, + position: String, + span: Rc, + module_name: String, +) -> Rc>> { + if ((crate::v1_compiler_infer_env::text_representation_is_text_arm(a.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(b.clone())) + || (crate::v1_compiler_infer_env::text_representation_is_text_arm(b.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(a.clone()))) + { + Rc::new(vec![crate::v1_std_core::make_error_node( + Rc::new( + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: position.clone(), + span: span.clone(), + }, + ), + module_name.clone(), + )]) + } else { + Rc::new(vec![]) + } +} + +pub fn text_representations_both_code_point(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::CodePointSequence => true, + _ => false, + }, + _ => false, + } +} + pub fn direct_call_arg_mismatch_diags( plan: Rc>>, typed_args: Rc>>, @@ -9123,11 +9247,29 @@ if match (*actual_expr.expr_data.clone()).clone() { { let actual_raw = expression_value_type(actual_expr.clone()); let actual = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(actual_raw.clone(), type_env.clone(), module_name.clone()); -if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { - Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) +let formal_text = if (formal_code_point_view(app.formal.clone(), type_env.clone()) != std::option::Option::None) { + TextRepresentation::CodePointSequence } else { - Rc::new(vec![]) - } + crate::v1_compiler_infer_env::text_representation_by_identity(formal.clone(), type_env.clone()) + }; +let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone()); +let unjudged = text_unjudged_advisories(formal_text.clone(), actual_text.clone(), "a call argument".to_string(), actual_expr.span.clone(), module_name.clone()); +v1_rt::concat(unjudged.clone(), if crate::v1_compiler_infer_env::text_representations_cross(formal_text.clone(), actual_text.clone()) { + Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::InternalError { + message: v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing at a call argument: declared ".to_string(), crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone())), ", produced ".to_string()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone())), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text".to_string()), + span: actual_expr.span.clone(), +}), module_name.clone())]) + } else { + if text_representations_both_code_point(formal_text.clone(), actual_text.clone()) { + Rc::new(vec![]) + } else { + if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { + Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) + } else { + Rc::new(vec![]) + } + } + }) } } }, @@ -9148,6 +9290,71 @@ pub struct Tier2bBt { pub kernel_diags: Rc>>, } +pub fn builtin_text_argument_crossing_diags( + func_name: String, + typed_args: Rc>>, + scope: Rc, +) -> Rc>> { + { + let host_params = + crate::v1_compiler_infer_method::builtin_host_text_param_names(func_name.clone()); + if ((host_params.clone().len() as i64) == 0) { + Rc::new(vec![]) + } else { + { + let formal_names = + crate::v1_compiler_infer_method::builtin_param_names(func_name.clone()); + Rc::new({ + let mut __result = Vec::new(); + for pair in Rc::new( + typed_args + .clone() + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (i as i64, v)) + .collect::>(), + ) + .iter() + .cloned() + { + __result.extend((*{ + let arg = pair.1.clone(); +let bound_name = match crate::v1_std_core::arg_name_at(arg.clone(), scope.type_env.clone().source_indices.clone()) { + Some(label) => label.clone(), + std::option::Option::None => match formal_names.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + Some(n) => n.clone(), + std::option::Option::None => "".to_string(), +}, +}; +if !{ let mut __found = false; for n in host_params.iter().cloned() { if (n.clone() == bound_name.clone()) { __found = true; break; } } __found } { + Rc::new(vec![]) + } else { + { + let value = crate::v1_std_core::arg_value(arg.clone()); +match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation(value.clone(), scope.type_env.clone()) { + TextRepresentation::CodePointSequence => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing: argument '".to_string(), bound_name.clone()), "' of ".to_string()), func_name.clone()), " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence".to_string()), value.span.clone(), scope.module_name.clone())]), + TextRepresentation::HostText => Rc::new(vec![]), + TextRepresentation::NotText => Rc::new(vec![]), + TextRepresentation::TextRepresentationUnidentified => Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: v1_rt::concat("argument '".to_string(), v1_rt::concat(bound_name.clone(), v1_rt::concat("' of ".to_string(), func_name.clone()))), + span: value.span.clone(), +}), scope.module_name.clone())]), +}, + _ => Rc::new(vec![]), +} +} + } +}).iter().cloned()); + } + __result + }) + } + } + } +} + pub fn infer_tier2b_builtin_with_kernel_diags( func_name: String, typed_args: Rc>>, @@ -9404,6 +9611,9 @@ pub fn infer_tier2b_builtin_with_kernel_diags( re.clone(), oe.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ), std::option::Option::None => false, }, @@ -11066,6 +11276,106 @@ pub fn expression_is_uppercase_constructor_reference( } } +pub fn destination_declares_scalar_sequence_unfold( + destination_type: Rc, + scope: Rc, +) -> bool { + match (*literal_boundary_elaboration( + Rc::new(LiteralValue::LitStr { + value: "".to_string(), + }), + Some(destination_type.clone()), + scope.clone(), + )) + .clone() + { + LiteralBoundary::LiteralBoundaryElaborated { elaboration: e, .. } => { + match (*e.homomorphism.clone().producer.clone()).clone() { + LiteralUnfolding::UnicodeScalarSequenceUnfold => true, + _ => false, + } + } + LiteralBoundary::LiteralBoundaryPlain => false, + LiteralBoundary::LiteralBoundaryRefused { message: _, .. } => false, + } +} + +pub fn host_text_unfolded_at_code_point_boundary( + value: Rc, + destination_type: Rc, + scope: Rc, +) -> Rc { + match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => { + match argument_text_representation(value.clone(), scope.type_env.clone()) { + TextRepresentation::HostText => { + if destination_declares_scalar_sequence_unfold( + destination_type.clone(), + scope.clone(), + ) { + crate::v1_std_core::make_named_expr_node( + Rc::new(NodeOccurrenceIdentity::OccurrenceSynthetic), + "chars".to_string(), + Rc::new(ExprData::ExprMethodCall { + method_semantics: Some(Rc::new( + MethodSemantics::PlainMethodSemantics, + )), + }), + Rc::new(vec![value.clone()]), + Some(Rc::new(InferredNode::Resolved { + node: destination_type.clone(), + })), + value.span.clone(), + elaborated_span("chars".to_string()), + ) + } else { + value.clone() + } + } + TextRepresentation::CodePointSequence => value.clone(), + TextRepresentation::NotText => value.clone(), + TextRepresentation::TextRepresentationUnidentified => value.clone(), + } + } + _ => value.clone(), + } +} + +pub fn formal_code_point_view(formal: Rc, env: Rc) -> Option> { + match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declared_type.clone(), + env.clone(), + ) { + TextRepresentation::HostText => std::option::Option::None, + _ => formal_code_point_view_peeled(formal.clone(), env.clone()), + } +} + +pub fn formal_code_point_view_peeled( + formal: Rc, + env: Rc, +) -> Option> { + match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declaration_bound_conformance.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declaration_bound_conformance.clone()), + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.substitution_basis.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.substitution_basis.clone()), + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declared_type.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declared_type.clone()), + _ => std::option::Option::None, + }, + }, + } +} + pub fn infer_call_arguments_generic_pass( call_args: Rc>>, value_params: Rc>>, @@ -11154,33 +11464,79 @@ pub fn infer_call_arguments_generic_pass( type_variable_node("callable_param".to_string()) } }; - let contextual_expected = + let formal_declaration_bound = match (*formal_selection.clone()).clone() { + CallArgumentFormalSelection::CallArgumentFormalSelected { + formal_index, .. + } => match formals + .clone() + .iter() + .cloned() + .skip(formal_index.clone() as usize) + .next() + { + Some(carried) => { + formal_code_point_view(carried.clone(), scope.type_env.clone()) + } + std::option::Option::None => std::option::Option::None, + }, + CallArgumentFormalSelection::CallArgumentFormalUnavailable => { + std::option::Option::None + } + }; + let formal_is_code_point_sequence = + (formal_declaration_bound.clone() != std::option::Option::None); + let code_point_formal = match formal_declaration_bound.clone() { + Some(bound) => bound.clone(), + std::option::Option::None => formal_conformance_type.clone(), + }; + let argument_is_literal = match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { - ExprData::ExprVar { - binding_kind: _, .. - } => { - if expression_is_uppercase_constructor_reference( - crate::v1_std_core::arg_value(a.clone()), - scope.type_env.clone().source_indices.clone(), - ) { - formal_conformance_type.clone() - } else { - formal_param_type.clone() + ExprData::ExprLiteral { value: _, .. } => true, + _ => false, + }; + let contextual_expected = + if (formal_is_code_point_sequence.clone() && argument_is_literal.clone()) { + code_point_formal.clone() + } else { + match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { + ExprData::ExprVar { + binding_kind: _, .. + } => { + if expression_is_uppercase_constructor_reference( + crate::v1_std_core::arg_value(a.clone()), + scope.type_env.clone().source_indices.clone(), + ) { + formal_conformance_type.clone() + } else { + formal_param_type.clone() + } } + ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), + _ => formal_param_type.clone(), } - ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), - _ => formal_param_type.clone(), }; let expected = if has_formal.clone() { Some(contextual_expected.clone()) } else { std::option::Option::None }; - let ar = infer_expr( + let ar_inferred = infer_expr( crate::v1_std_core::arg_value(a.clone()), scope.clone(), expected.clone(), ); + let ar = if formal_is_code_point_sequence.clone() { + Rc::new(InferResult { + typed: host_text_unfolded_at_code_point_boundary( + ar_inferred.typed.clone(), + code_point_formal.clone(), + scope.clone(), + ), + diagnostics: ar_inferred.diagnostics.clone(), + }) + } else { + ar_inferred.clone() + }; let next_subst = if (is_lambda_expr(crate::v1_std_core::arg_value(a.clone())) || !has_formal.clone()) { @@ -11428,6 +11784,10 @@ pub enum LiteralBoundary { }, } +pub fn code_point_sequence_structure_ref() -> Rc { + crate::std_decl_ref::decl_ref("std.algebra".to_string(), "FreeMonoid".to_string()) +} + pub fn literal_boundary_elaboration( lit: Rc, expected: Option>, @@ -11439,36 +11799,57 @@ pub fn literal_boundary_elaboration( if (exp.return_cardinality.clone() == Cardinality::CardOptional) { Rc::new(LiteralBoundary::LiteralBoundaryPlain) } else { - match crate::v1_compiler_infer_env::type_reference_declaration_ref( - exp.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) { - std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), - Some(destination) => { - let kind = - crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); - let element = if ((exp.children.clone().len() as i64) == 1) { - match exp.children.clone().first().cloned() { - Some(arg) => { - crate::v1_compiler_infer_env::type_reference_declaration_ref( - arg.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) - } - std::option::Option::None => std::option::Option::None, - } - } else { - std::option::Option::None + { + let is_code_point_sequence = + match crate::v1_compiler_infer_env::text_representation_by_identity( + exp.clone(), + scope.type_env.clone(), + ) { + TextRepresentation::CodePointSequence => true, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, }; - let natively = crate::v1_compiler_coercion::provenance_realizes_natively( - crate::v1_compiler_infer_env::declaration_provenance_of_ref( - destination.clone(), - scope.type_env.clone(), - ), - ); - match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { + let identified = if is_code_point_sequence.clone() { + Some(code_point_sequence_structure_ref()) + } else { + crate::v1_compiler_infer_env::type_reference_declaration_ref( + exp.clone(), + scope.type_env.clone().source_indices.clone(), + scope.type_env.clone(), + ) + }; + match identified.clone() { + std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), + Some(destination) => { + let kind = + crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); + let element = if is_code_point_sequence.clone() { + Some(crate::std_decl_ref::decl_ref( + "std.types".to_string(), + "Char".to_string(), + )) + } else { + if ((exp.children.clone().len() as i64) == 1) { + match exp.children.clone().first().cloned() { + Some(arg) => crate::v1_compiler_infer_env::type_reference_declaration_ref(arg.clone(), scope.type_env.clone().source_indices.clone(), scope.type_env.clone()), + std::option::Option::None => std::option::Option::None, +} + } else { + std::option::Option::None + } + }; + let natively = if is_code_point_sequence.clone() { + false + } else { + crate::v1_compiler_coercion::provenance_realizes_natively( + crate::v1_compiler_infer_env::declaration_provenance_of_ref( + destination.clone(), + scope.type_env.clone(), + ), + ) + }; + match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { LiteralElaborationOutcome::DirectLiteral => Rc::new(LiteralBoundary::LiteralBoundaryPlain), LiteralElaborationOutcome::ViaHomomorphism { homomorphism: h, .. } => Rc::new(LiteralBoundary::LiteralBoundaryElaborated { elaboration: Rc::new(LiteralElaboration { @@ -11482,6 +11863,7 @@ pub fn literal_boundary_elaboration( message: crate::std_literal_elaboration::literal_elaboration_refusal_message(c.clone()), }), } + } } } } @@ -13109,7 +13491,7 @@ Rc::new(InferResult { }), typed_arg_nodes.clone(), Some(Rc::new(InferredNode::Resolved { node: bt.clone(), })), span.clone(), crate::v1_std_core::node_name_span(texpr.clone())), - diagnostics: v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), + diagnostics: v1_rt::concat(v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), builtin_text_argument_crossing_diags(func_name.clone(), typed_args.clone(), scope.clone())), }) } } else { @@ -13951,6 +14333,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), then_rt.clone(), else_rt.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) { Rc::new(vec![]) } else { @@ -14920,6 +15305,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )), _ => std::option::Option::None, }, @@ -14987,6 +15375,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )) } _ => std::option::Option::None, @@ -16753,7 +17144,9 @@ if direct_call_formal_has_unbound_type_variable(field_conformance_type.clone()) if ((field_cardinality_diags.clone().len() as i64) > 0) { field_cardinality_diags.clone() } else { - if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone()) { + if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone(), Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), +})) { Rc::new(vec![]) } else { if declared_alias_target_matches_produced(expected_node.clone(), got_node.clone(), scope.type_env.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_infer_env.rs b/src/v1/stage0/src/v1_compiler_infer_env.rs index 7ac2f7038bb..5c2b1e524c2 100644 --- a/src/v1/stage0/src/v1_compiler_infer_env.rs +++ b/src/v1/stage0/src/v1_compiler_infer_env.rs @@ -7,10 +7,15 @@ use self::TypeNodeUnidentifiedCause::*; use self::TypeReferenceDeclarationReading::*; use self::UnitVariantPhantomLookup::*; pub use crate::std_algebra::FreeMonoid; -pub use crate::std_coercion::TypeDeclarationProvenance; -use crate::std_coercion::TypeDeclarationProvenance::DeclarationIdentityAbsent; -pub use crate::std_decl_ref::decl_ref; +use crate::std_coercion::TextRepresentation::{ + CodePointSequence, HostText, NotText, TextRepresentationUnidentified, +}; +use crate::std_coercion::TypeDeclarationProvenance::{ + CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, +}; +pub use crate::std_coercion::{TextRepresentation, TypeDeclarationProvenance}; pub use crate::std_decl_ref::DeclarationRef; +pub use crate::std_decl_ref::{decl_ref, declaration_ref_display_key, declaration_ref_eq}; use crate::std_induction::RecursionShape::{ DirectRecursion, ListRecursion, MapValueRecursion, OptionalRecursion, SetRecursion, }; @@ -34,10 +39,10 @@ use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; use crate::v1_std_core::Cardinality::*; use crate::v1_std_core::CompilerDiagnostic::{AmbiguousReference, UnresolvedType}; -use crate::v1_std_core::Connective::*; +use crate::v1_std_core::Connective::NoConnective; use crate::v1_std_core::DeclarationMarker::Unmarked; use crate::v1_std_core::ExprData::*; -use crate::v1_std_core::InferredNode::Resolved; +use crate::v1_std_core::InferredNode::{CompilerError, Resolved, TypeVariable}; pub use crate::v1_std_core::ParsedModuleItemKind; use crate::v1_std_core::ParsedModuleItemKind::*; pub use crate::v1_std_core::{ @@ -1674,6 +1679,14 @@ pub fn record_global_bare_ambiguous_silent_pick( } pub fn global_bare_lookup(env: Rc, name: String) -> Option> { + if crate::std_types::is_kernel_type(name.clone()) { + std::option::Option::None + } else { + global_bare_lookup_candidates(env.clone(), name.clone()) + } +} + +pub fn global_bare_lookup_candidates(env: Rc, name: String) -> Option> { match v1_rt::map_get(&env.symbol_index.clone().global_bare.clone(), name.clone()) .as_deref() .cloned() @@ -2837,6 +2850,373 @@ pub fn declaration_provenance_of_ref( } } +pub fn text_identity_std_algebra_free_monoid() -> Rc { + crate::std_decl_ref::decl_ref("std.algebra".to_string(), "FreeMonoid".to_string()) +} + +pub fn text_identity_std_types_char() -> Rc { + crate::std_decl_ref::decl_ref("std.types".to_string(), "Char".to_string()) +} + +pub fn node_is_kernel_mint(n: Rc) -> bool { + match (*crate::v1_std_core::declaration_provenance_of(n.clone())).clone() { + TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + } +} + +pub fn node_is_kernel_mint_named(n: Rc, name: String) -> bool { + match (*crate::v1_std_core::declaration_provenance_of(n.clone())).clone() { + TypeDeclarationProvenance::KernelMinted { minted_name: m, .. } => { + (m.clone() == name.clone()) + } + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::DeclarationIdentityAbsent => false, + } +} + +pub fn declaration_binding_of_ref( + d: Rc, + env: Rc, +) -> Option> { + match v1_rt::map_get( + &env.symbol_index.clone().global_bare.clone(), + d.decl_name.clone(), + ) + .as_deref() + .cloned() + { + Some(GlobalBareLookupState::GlobalBareUniqueBinding { + module_path: mp, + binding: b, + .. + }) => { + if (mp.clone() == d.module_path.clone()) { + Some(b.clone()) + } else { + std::option::Option::None + } + } + Some(GlobalBareLookupState::GlobalBareAmbiguousBinding { + candidates: cands, .. + }) => match Rc::new({ + let mut __result = Vec::new(); + for c in cands.iter().cloned() { + if (c.module_path.clone() == d.module_path.clone()) { + __result.push(c); + } + } + __result + }) + .first() + .cloned() + { + Some(c) => Some(c.binding.clone()), + std::option::Option::None => std::option::Option::None, + }, + std::option::Option::None => std::option::Option::None, + } +} + +pub fn text_element_is_char(n: Rc, env: Rc) -> TextRepresentation { + match n.children.clone().first().cloned() { + std::option::Option::None => TextRepresentation::TextRepresentationUnidentified, + Some(ch) => { + let el = match ch.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: r, .. }) => r.clone(), + _ => ch.clone(), + }; + match (*type_reference_declaration_reading( + el.clone(), + env.source_indices.clone(), + env.clone(), + )) + .clone() + { + TypeReferenceDeclarationReading::TypeReferenceNamesDeclaration { + declaration: e, + .. + } => { + if crate::std_decl_ref::declaration_ref_eq( + e.clone(), + text_identity_std_types_char(), + ) { + TextRepresentation::CodePointSequence + } else { + TextRepresentation::NotText + } + } + TypeReferenceDeclarationReading::TypeReferenceIsKernelType { name: _, .. } => { + TextRepresentation::NotText + } + TypeReferenceDeclarationReading::TypeReferenceIsTypeVariable { id: _, .. } => { + TextRepresentation::NotText + } + TypeReferenceDeclarationReading::TypeReferenceUnidentified { .. } => { + TextRepresentation::TextRepresentationUnidentified + } + } + } + } +} + +pub fn text_representation_by_identity(n: Rc, env: Rc) -> TextRepresentation { + text_representation_by_identity_bounded(n.clone(), env.clone(), 16, Rc::new(vec![])) +} + +pub fn text_representation_by_identity_bounded( + mut __tco_loop_n: Rc, + mut __tco_loop_env: Rc, + mut __tco_loop_fuel: i64, + mut __tco_loop_visited: Rc>, +) -> TextRepresentation { + loop { + #[allow(unused_mut)] + let mut n = __tco_loop_n; + #[allow(unused_mut)] + let mut env = __tco_loop_env; + #[allow(unused_mut)] + let mut fuel = __tco_loop_fuel; + #[allow(unused_mut)] + let mut visited = __tco_loop_visited; + let resolved = match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: r, .. }) => Some(r.clone()), + _ => std::option::Option::None, + }; + let kernel_string = (node_is_kernel_mint_named(n.clone(), "String".to_string()) + || match resolved.clone() { + Some(r) => node_is_kernel_mint_named(r.clone(), "String".to_string()), + std::option::Option::None => false, + }); + let generic_or_error = match n.inferred.clone().as_deref().cloned() { + Some(InferredNode::TypeVariable { id: _, .. }) => true, + Some(InferredNode::CompilerError { .. }) => true, + _ => false, + }; + let kernel_other = (node_is_kernel_mint(n.clone()) + || match resolved.clone() { + Some(r) => node_is_kernel_mint(r.clone()), + std::option::Option::None => false, + }); + if kernel_string.clone() { + break TextRepresentation::HostText; + } else { + if generic_or_error.clone() { + break TextRepresentation::NotText; + } else { + if (fuel.clone() <= 0) { + break TextRepresentation::TextRepresentationUnidentified; + } else { + if is_where_refinement_node(n.clone()) { + match n.children.clone().first().cloned() { + Some(base) => { + let __tco_0 = base.clone(); + let __tco_1 = env; + let __tco_2 = v1_rt::int_sub(fuel, 1); + let __tco_3 = visited; + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = __tco_3; + continue; + } + std::option::Option::None => { + break TextRepresentation::TextRepresentationUnidentified; + } + } + } else { + match (*type_reference_declaration_reading( + n.clone(), + env.source_indices.clone(), + env.clone(), + )) + .clone() + { + TypeReferenceDeclarationReading::TypeReferenceUnidentified { + .. + } => { + if kernel_other.clone() { + break TextRepresentation::NotText; + } else { + break TextRepresentation::TextRepresentationUnidentified; + } + } + TypeReferenceDeclarationReading::TypeReferenceIsKernelType { + name: k, + .. + } => { + if (k.clone() == "String".to_string()) { + break TextRepresentation::HostText; + } else { + break TextRepresentation::NotText; + } + } + TypeReferenceDeclarationReading::TypeReferenceIsTypeVariable { + id: _, + .. + } => { + break TextRepresentation::NotText; + } + TypeReferenceDeclarationReading::TypeReferenceNamesDeclaration { + declaration: d, + .. + } => { + if crate::std_decl_ref::declaration_ref_eq( + d.clone(), + text_identity_std_algebra_free_monoid(), + ) { + break text_element_is_char(n.clone(), env.clone()); + } else { + let key = + crate::std_decl_ref::declaration_ref_display_key(d.clone()); + if { + let mut __found = false; + for v in visited.iter().cloned() { + if (v.clone() == key.clone()) { + __found = true; + break; + } + } + __found + } { + break TextRepresentation::TextRepresentationUnidentified; + } else { + match declaration_binding_of_ref(d.clone(), env.clone()) { + std::option::Option::None => { + break TextRepresentation::TextRepresentationUnidentified; + } + Some(b) => { + let decl = b.resolved.clone(); + let is_alias = ((decl.connective.clone() + == Connective::NoConnective) + && ((decl.children.clone().len() as i64) == 0)); + if is_where_refinement_node(decl.clone()) { + { + let __tco_0 = decl.clone(); + let __tco_1 = env; + let __tco_2 = v1_rt::int_sub(fuel, 1); + let __tco_3 = v1_rt::concat( + visited, + Rc::new(vec![key.clone()]), + ); + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = __tco_3; + continue; + } + } else { + if !is_alias.clone() { + break TextRepresentation::NotText; + } else { + match decl + .inferred + .clone() + .as_deref() + .cloned() + { + Some(InferredNode::Resolved { + node: target, + .. + }) => { + let target_is_itself = match (*type_reference_declaration_reading(target.clone(), env.source_indices.clone(), env.clone())).clone() { + TypeReferenceDeclarationReading::TypeReferenceNamesDeclaration { declaration: t, .. } => crate::std_decl_ref::declaration_ref_eq(t.clone(), d.clone()), + _ => false, +}; + if target_is_itself.clone() { + break TextRepresentation::NotText; + } else { + { + let __tco_0 = + target.clone(); + let __tco_1 = env; + let __tco_2 = + v1_rt::int_sub(fuel, 1); + let __tco_3 = v1_rt::concat( + visited, + Rc::new(vec![ + key.clone() + ]), + ); + __tco_loop_n = __tco_0; + __tco_loop_env = __tco_1; + __tco_loop_fuel = __tco_2; + __tco_loop_visited = + __tco_3; + continue; + } + } + } + _ => { + break TextRepresentation::NotText; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn is_where_refinement_node(n: Rc) -> bool { + (((n.connective.clone() == Connective::Conj) + && (n.type_annotation.clone() != std::option::Option::None)) + && ((n.children.clone().len() as i64) == 1)) +} + +pub fn text_representation_is_unidentified(r: TextRepresentation) -> bool { + match r.clone() { + TextRepresentation::TextRepresentationUnidentified => true, + TextRepresentation::HostText => false, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + } +} + +pub fn text_representation_is_text_arm(r: TextRepresentation) -> bool { + match r.clone() { + TextRepresentation::HostText => true, + TextRepresentation::CodePointSequence => true, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, + } +} + +pub fn text_crossing_by_identity(left: Rc, right: Rc, env: Rc) -> bool { + text_representations_cross( + text_representation_by_identity(left.clone(), env.clone()), + text_representation_by_identity(right.clone(), env.clone()), + ) +} + +pub fn text_representations_cross(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::HostText => match b.clone() { + TextRepresentation::CodePointSequence => true, + TextRepresentation::TextRepresentationUnidentified => false, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::HostText => true, + TextRepresentation::TextRepresentationUnidentified => false, + TextRepresentation::CodePointSequence => false, + TextRepresentation::NotText => false, + }, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, + } +} + pub fn type_reference_declaration_ref( n: Rc, source_indices: Rc>>, diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index 30961dd1eda..f0a03f3d2a9 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -7,7 +7,9 @@ use crate::std_algebra::AlgebraTypeTemplate::{ ReceiverKey, ReceiverSelf, ReceiverValue, TupleOf, WitnessOf, }; use crate::std_algebra::ContainerSource::{Named, SameAsReceiver}; -pub use crate::std_algebra::{algebra_templates_for_profile, all_algebra_profiles}; +pub use crate::std_algebra::{ + algebra_templates_for_profile, all_algebra_profiles, free_monoid_scalar_templates, +}; pub use crate::std_algebra::{ AlgebraFieldTemplate, AlgebraProfile, AlgebraTypeTemplate, ContainerSource, }; @@ -2092,6 +2094,64 @@ pub fn builtin_signature(name: String) -> Option> { v1_rt::map_get(&builtin_function_registry(), name.clone()) } +pub fn builtin_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + Some(sig) => Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + __result.push(p.name.clone()); + } + __result + }), + std::option::Option::None => Rc::new(vec![]), + } +} + +pub fn builtin_host_text_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + std::option::Option::None => Rc::new(vec![]), + Some(sig) => { + let scalar_string_method = { + let mut __found = false; + for t in crate::std_algebra::free_monoid_scalar_templates() + .iter() + .cloned() + { + if (t.name.clone() == name.clone()) { + __found = true; + break; + } + } + __found + }; + Rc::new({ + let mut __result = Vec::new(); + for p in Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + if match (*p.ty.clone()).clone() { + AlgebraTypeTemplate::NamedTemplate { name: n, .. } => { + (n.clone() == "String".to_string()) + } + AlgebraTypeTemplate::ReceiverSelf => scalar_string_method.clone(), + _ => false, + } { + __result.push(p); + } + } + __result + }) + .iter() + .cloned() + { + __result.push(p.name.clone()); + } + __result + }) + } + } +} + pub fn infer_builtin_call_type(name: String) -> Option> { match builtin_signature(name.clone()) { Some(sig) => Some(sig.returns.clone()), From f4129bd6f9e9d9113eedaa1bb97d84bd2fdcbfc9 Mon Sep 17 00:00:00 2001 From: neat-boar-16 Date: Fri, 2 Oct 2026 07:50:47 +0000 Subject: [PATCH 20/24] Regenerate docs/design-rung-drops.md (docs_projection_gate regen, gunbc sha256 08f520bec5a7, srv1) --- docs/design-rung-drops.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 761662a75cf..53a1d0c2130 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -110,7 +110,9 @@ AMENDED 2026-10-01: the srv1 probe of the withdrawn v2.test. family (neat-boar-1 ### Non-literal kernel-String refusal at the structural text boundary — declared 2026-08-30 -**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. +**CURRENT STANDING (2026-09-29, after the CLIMB below): a text crossing between two IDENTIFIED sides is structurally guaranteed -- a non-literal kernel-String value at an identified code-point-sequence boundary unfolds through the declared row or refuses, located. This row stands for exactly two remaining populations, each with its own trigger stated below: (1) CALLABLE-SIGNATURE crossings -- a callable whose parameters are code-point sequences passed where a callable over host text is expected, which callable-signature agreement does not yet judge by the text classifier; (2) the UNIDENTIFIED population -- a boundary where one side has no readable declaration identity, which the wall declines to judge (STANDING RESTATED, at the end). The Rust renderer's separate text-carrier answer is its own row, gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall.** + +HISTORY, AS DECLARED 2026-08-30 (the structural-text landing), superseded by the CLIMB below -- kept as history, not as standing: **A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DID NOT REFUSE.** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINED ADMITTED at that boundary (until the CLIMB) and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. POPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses. @@ -126,6 +128,10 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT this authority: it keeps a provenance-keyed answer, a second authority for the same fact, declared as its own typed stall row gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall with its population and trigger. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. + +STANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. The identity each row reports as unread is v1's ONE type-reference derivation, `v1.compiler.infer_env` `type_reference_declaration_reading` (its `TypeReferenceUnidentified` arm), the same read `v1.tests.claim.carrier_realization_census` records per authored reference position; the census is NOT this population's producer, because it enumerates authored type-reference positions inside declarations, while this population is checker comparisons at use sites whose value side is usually an inferred type with no authored position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text. + ### Fabric CI evidence lane as a required merge block, and then as a lane at all — declared 2026-08-31 **CORRECTED 2026-09-28: THE CALIBRATION SCRIPT WAS DELETED AND THEN RESTORED, AND THE LANE WAS NOT.** The amendment below said the 2026-09-04 ruling deleted `tools/fabric_ci_evidence_calibration.sh`. #10390 did delete it on 2026-09-04. #10270 (FCI-1) re-added it on 2026-09-05, and it is live: `tools/fabric_ci_fci1_live_instrument.sh` runs it against the exact binary before any srv3 observation. The original sentence is QUOTED, not deleted, because the record of what was claimed is the point: 'together with fabric_ci_evidence_calibration_step, its two derived bounds, and tools/fabric_ci_evidence_calibration.sh'. WHAT THE CORRECTION CHANGES: only the script clause. The script now runs only when an operator invokes the FCI-1 live instrument by hand, on no CI trigger. WHAT IT DOES NOT CHANGE: the `fabric-evidence` job, the calibration step and the FABRIC_EVIDENCE binding are still absent from every emitted workflow. `w_RED_the_deleted_lanes_do_not_return` still guards that absence. A red from a hand-invoked run is still not a merge wall, so the temporary rung (outside the modeled guarantee for merges), the population and the restoration trigger all stand unchanged. The script is its own scaffold with its own dissolution condition in its header; this row does not govern it. **AMENDED 2026-09-04: THE LANE NO LONGER EXECUTES EITHER, AND THIS ROW NOW COVERS BOTH LOSSES.** The 2026-09-04 operator ruling on runner capacity deleted the `fabric-evidence` JOB, together with `fabric_ci_evidence_calibration_step`, its two derived bounds, and `tools/fabric_ci_evidence_calibration.sh`. The declaration below was written when only the merge block was withdrawn and the calibration still ran; read every present-tense claim in it about the lane EXECUTING in the past tense. WHAT CHANGES: the temporary rung falls from mitigatable to outside the modeled guarantee — there is no longer a red for a human to read, because there is no run. WHAT DOES NOT: the restoration trigger, which was already stated at capability grain and is unchanged, and the operational consequence, which now binds harder — the FABRIC-CI program (warm-seal-35) agreed to check `fabric-evidence` explicitly on every head rather than inferring it from `witnesses`, and there is now nothing to check, so any conclusion resting on that lane rests on nothing. THE ROW IS AMENDED RATHER THAN SUPERSEDED because the subject is the same lane and the same trigger retires both losses; a second row would have split one subject across two authorities. The countability moved: `w_RED_fabric_evidence_executes_without_gating` asserted the lane still ran and is replaced by `test.claim.deleted_lanes_countable_witness_test` `w_RED_the_deleted_lanes_do_not_return`, which reds if the job, the FABRIC_EVIDENCE binding or the calibration argv returns while this row stands. The original declaration follows. **THE FABRIC-CI EVIDENCE LANE STOPS BLOCKING MERGES AND KEEPS EXECUTING (2026-08-31).** #9704 added `fabric-evidence` as a fourth job and, in the same change, made it a `needs` of the aggregating `witnesses` job that the `passing CI` ruleset names as this repository's one required context. PREVIOUS RUNG: mechanically preventable — a defect in the FCI-EVIDENCE-0 run-boundary contract was exposed by an enrolled calibration and blocked the merge. TEMPORARY RUNG: mitigatable — the same calibration still runs on every push and pull request, still refuses loudly, and its log is still the record; what is gone is the block, so a red is now a signal a human must read rather than a wall. REASON, and it is wall clock rather than doubt about the evidence: the required context's wall is the MAX over its three lanes, and on run 33350499023 that max was `fabric-evidence` at 27 minutes against 20 for the floor lane and 10.5 for the build lane, having moved the required wall from ~13 minutes to ~28-40 within a day of #9704 landing. 1469 of those 1620 seconds are the calibration step, and its own logs put all ~18 of the plan's `gunbc run` host processes at 80-90 seconds each. That cost is not the evidence: the instrument's import closure is six modules, and per `cli_run.entry_resolve` an `--entry` run is scoped in what it EMITS but WHOLE-TREE in what it PARSES, so each process re-parses and re-censuses the entire 4414-module corpus to evaluate a handful of rows. The lane is paying eighteen whole-corpus censuses (DESIGN §2 duplicated work) for thirteen assertions. Batching them is NOT the repair and is not what this row defers: the calibration-timeout derivation recorded that distinct host processes ARE the process-edge subject (that row was deleted with the lane on 2026-09-04; its reasoning is preserved in this sentence), so fewer processes would remove the boundary rather than observe it more cheaply. BOUNDED POPULATION: exactly one job, `fabric-evidence`, and exactly one lost edge — its `needs.fabric-evidence.result` binding into the aggregate's closed-vocabulary verdict fold. No other lane, phase, witness or claim changes rung; the build and floor lanes keep every edge they had, and the calibration keeps every assertion it had. The drop is countable from the emitted workflow: `test.claim.witness_floor_workflow_consolidation_witness_test` `w_RED_fabric_evidence_executes_without_gating` reds if the job stops executing, and reds again if either gating spelling returns while this row still stands. ONE OPERATIONAL CONSEQUENCE, recorded here because it is the kind of practice that quietly stops once the person who agreed to it moves on. With the needs edge withdrawn, a `fabric-evidence` failure no longer reaches the aggregate verdict, so any lane that was reading the required context as a proxy for that lane's health is now reading a context that cannot see it. The FABRIC-CI program (warm-seal-35) accepted the drop on that basis and will check `fabric-evidence` EXPLICITLY on every head rather than inferring it from `witnesses`. Anyone else joining on the required context owes the same explicit check for as long as this row stands; that obligation ends when the trigger below is met and not before. RESTORATION TRIGGER: the CAPABILITY of a single fabric evidence process resolving its entry against the source roots at a cost that fits the required wall budget — concretely, the whole-tree parse and name census being paid once per tree state rather than once per host process, so that the 13-row plan executes its full process-edge roster inside the lane's share of a ~10-minute required wall. THIS IS NOT RETIRED BY THE MEMO PR MERGING. It is retired by a measured `fabric-evidence` job wall that leaves the required context at or under its budget with the needs edge and the FABRIC_EVIDENCE binding restored, which is the only thing that makes the block affordable again. Reducing the plan's process count, relaxing what a row asserts, or moving the calibration to a cadence does not satisfy this trigger — the first removes the subject, and the other two are different drops that would need their own rows. From 735acb0452ae2f33ddd0cd31e89ec5426d471520 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 2 Oct 2026 08:39:19 +0000 Subject: [PATCH 21/24] Retire coreutils_stat/sha256sum #get bare-provider debt rows as ImportsFixed (floor RosterStale: #12910's collect_reference_sites no longer reports the pair in these PR-touched files) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../workflow/floor_unimported_bare_provider_debt_roster.dag | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v2/workflow/floor_unimported_bare_provider_debt_roster.dag b/src/v2/workflow/floor_unimported_bare_provider_debt_roster.dag index 51e25d2feb8..9f54b8a821f 100644 --- a/src/v2/workflow/floor_unimported_bare_provider_debt_roster.dag +++ b/src/v2/workflow/floor_unimported_bare_provider_debt_roster.dag @@ -331,12 +331,12 @@ data unimported_bare_provider_dispositions: List Date: Fri, 2 Oct 2026 09:26:27 +0000 Subject: [PATCH 22/24] Regenerate docs (docs_projection_gate regen on fce556f440, srv1) --- docs/design-rung-drops.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 32baf9629e4..3b1eb3f8804 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -110,7 +110,9 @@ AMENDED 2026-10-01: the srv1 probe of the withdrawn v2.test. family (neat-boar-1 ### Non-literal kernel-String refusal at the structural text boundary — declared 2026-08-30 -**A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DOES NOT REFUSE, AND THIS ROW DECLARES THAT RUNG (2026-08-30, the structural-text landing).** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINS ADMITTED at that boundary and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. +**CURRENT STANDING (2026-09-29, after the CLIMB below): a text crossing between two IDENTIFIED sides is structurally guaranteed -- a non-literal kernel-String value at an identified code-point-sequence boundary unfolds through the declared row or refuses, located. This row stands for exactly two remaining populations, each with its own trigger stated below: (1) CALLABLE-SIGNATURE crossings -- a callable whose parameters are code-point sequences passed where a callable over host text is expected, which callable-signature agreement does not yet judge by the text classifier; (2) the UNIDENTIFIED population -- a boundary where one side has no readable declaration identity, which the wall declines to judge (STANDING RESTATED, at the end). The Rust renderer's separate text-carrier answer is its own row, gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall.** + +HISTORY, AS DECLARED 2026-08-30 (the structural-text landing), superseded by the CLIMB below -- kept as history, not as standing: **A NON-LITERAL KERNEL-STRING VALUE AT AN EXACT `v2.std.text.String` BOUNDARY DID NOT REFUSE.** The literal path is walled: a kernel string LITERAL at that boundary elaborates through the one declared homomorphism row (`gunbc.structural_realization_bindings` `literal_homomorphism_rows`, `UnicodeScalarSequenceUnfold`) or refuses, keyed by occurrence, and the elaboration is carried on the typed tree into emission. What is NOT walled is the non-literal case: a kernel-String VALUE — a variable, a call result — reaching an exact `v2.std.text.String` position is still admitted by same-leaf-name compatibility in the corpus-wide type-compatibility relation (`v1.compiler.infer_types` `node_type_equals_core` and the `type_name_compatible` last-segment arm), so compat-by-leaf-name REMAINED ADMITTED at that boundary (until the CLIMB) and a host-representation value can flow into a structural-carrier position with no homomorphism and no diagnostic until emission or runtime. PREVIOUS RUNG, STATED HONESTLY: none stood. An identity wall was authored into the four comparison arms and EXECUTED ONCE as an experimental wall on this branch, then WITHDRAWN for a fabricated refusal before reaching main — it is recorded as an attempted wall withdrawn, never as a guarantee that stood, because a rung is established by evidence executing on the acceptance path and this one's first execution was its falsification. TEMPORARY STANDING, AT TWO PATHS THAT MUST NOT BE AVERAGED: on the emitted-Rust self-host path the class is *mechanically preventable* — a host-representation value flowing into a structural-carrier position renders as a Rust type mismatch and the mandatory emitted-crate cargo check refuses it; on the generic source-acceptance path the class is presently UNGUARDED — an open conformance defect, not a mitigated one, since no executing mechanism observes it there and review diligence is not a rung. REASON FOR THE WITHDRAWAL, measured not argued: the wall's first required-floor run refused GROUNDED-IDENTITY code — `dag/gunbc/roadmap_page.dag` line 386's if-join red with `Node(String) vs Primitive(String)` — because the corpus-wide relation cannot distinguish 'same declaration reached through the String = FreeMonoid identity that operator Ruling 3 (2026-07-15) grounds in std.coercion' from 'two unrelated declarations sharing a leaf name'. A wall that refuses code the design rules correct is a fabricated refusal, which §5 forbids in the same breath as a fabricated green. POPULATION, BOUNDED BY IDENTITY WITH A NAMED PRODUCER: the subject rows are non-literal values whose actual declaration is the kernel/native String offered at a destination whose exact identity is v2.std.text.String, on the current tree; the producer is the withdrawn wall itself re-applied as a measurement — the identity-wall patch on the four comparison arms, executed by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag` over the emitted-compiler closure and by the required floor's strict preparation — with each refusal row carrying its boundary identity and a disposition. Grounded std.string_type.String pairings (the roadmap_page if-join class, admitted under Ruling 3's declared identity) are a SEPARATE disposition and are not counted in the gap; literal rows are discharged by the homomorphism and are not in the subject. Any actual undispositioned row inside the emitted compiler closure blocks the parent class's closure; zero is a valid measured answer and 'unknown' is not a bound. RESTORATION PROBE, KEPT RATHER THAN DELETED: a planted non-literal fixture stands enrolled whose PRESENT observation is that the compiler accepts the row-less non-literal boundary — deleting it would turn a bounded gap into an unobserved one; on restoration it flips to the discriminating refusal control. RESTORATION TRIGGER, naming the capability: the declared-boundary conformance PEELING capability — the compatibility relation answering 'does this value's declared type reach that boundary's declared type through a DECLARED identity or homomorphism row' by peeling declared identities (String = FreeMonoid per Ruling 3) rather than by leaf-name spelling. That capability is SUFFICIENT only as a two-direction A/B in ONE required-floor run: the planted non-literal fixture REFUSES; roadmap_page's grounded if-join stays ADMITTED; an ordinary grounded-identity non-literal control stays ADMITTED; and the literal homomorphism fixtures stay admitted and still emit scalar sequences. Passing the refusal direction by restoring the roadmap_page false positive is NOT the trigger. Re-landing the wall under that A/B retires this row; nothing else does — in particular, the literal-path witness battery going green does not, because its subject is the other half of the class. POPULATION CORRECTION, 2026-09-01, MEASURED: THE SUBJECT IS WIDER THAN THE PARAGRAPH ABOVE STATES, AND THE RESTORATION TRIGGER AS WRITTEN IS SATISFIABLE WHILE THE HOLE STAYS OPEN. The population above is bounded to destinations whose EXACT identity is v2.std.text.String. That bound holds only for the unaliased spelling. Measured by session bold-carp-449 on a four-arm fixture, one file and one resolve per pair so no arm is attributable to a different tree state or binary: a kernel String actual at a parameter declared v2.std.text.String REFUSES (declared Node(std.algebra.FreeMonoid), produced Primitive(String)); the same actual at a parameter declared with a locally-authored structure-identical alias, type Wrapped = FreeMonoid, is ADMITTED; and — the arm that decides the width — the same actual at a parameter declared LocalText where type LocalText = v2.std.text.String, an alias whose target IS the guarded declaration, is ALSO ADMITTED, against a cross-module control in the same file that refuses. @@ -126,6 +128,10 @@ TWO QUESTIONS THIS ROW DOES NOT ANSWER AND WHOEVER CLIMBS IT MUST: whether the r CORRECTION NOTICE, recorded because the wrong version travelled: an earlier revision of this paragraph asserted that kernel precedence beats LOCALS and that v2.std.text does not get its own spelling back. That was inferred from a single foreign-scope observation (string_head refusing a scalar sequence with declared Primitive(String)) and the in-module arm falsifies it. The observation was correct and the explanation was not: the module resolving the signature was the OBSERVER'S, not the declarer's. +CLIMB, 2026-09-28 (royal-newt-820, XL-0T ruling B relayed by neat-boar-16 msg_c69ec34e: one type, two representations, every crossing through the Unicode scalar unfold or a refusal). WHAT CLIMBED, BY EXECUTION: the value-at-boundary subject of this row. Compatibility no longer keys on the leaf spelling: text representation is decided by DECLARATION IDENTITY (v1.compiler.infer_env text_representation_by_identity; std.coercion TextRepresentation = HostText | CodePointSequence | NotText | TextRepresentationUnidentified, review 72252). HostText is the kernel String mint. CodePointSequence is the declaration std.algebra FreeMonoid applied to the declaration std.types Char, directly or through transparent aliases. A where-refinement has its base's representation, which is why std.types NonEmptyStr is host text. Identity is read only by declaring span, by qualified name, or, inside a declaration's body, by that declaration's own module, and never by re-resolving a spelling in the comparing site's scope. It is read by the corpus relation (v1.compiler.infer_types node_type_equals_core and node_type_compatible, which now carry the TypeEnv), by the call-argument judgment (v1.compiler.infer direct_call_arg_mismatch_diags), by declared-return and data-initializer conformance (declared_type_conformance_diags), and by the builtin argument check (builtin_text_argument_crossing_diags -- builtins were admitted with UNTYPED arguments, which is how std.string_type's string_length/char_at/substring passed). The Rust renderer (v1.compiler.emit_rust is_host_text_carrier_type) is NOT this authority: it keeps a provenance-keyed answer, a second authority for the same fact, declared as its own typed stall row gunbc.guarantee_stall text_carrier_render_not_keyed_on_identity_stall with its population and trigger. A host value at a code-point-sequence call argument is UNFOLDED as a typed-tree node -- the scalar-string algebra method chars, typed as the destination, gated on the existing gunbc.structural_realization_bindings literal_homomorphism_rows UnicodeScalarSequenceUnfold row (v1.compiler.infer destination_declares_scalar_sequence_unfold); every other crossing refuses, located. THE CALLER-RE-RESOLUTION ESCAPE IS CLOSED, and that is this row's trigger clause discharged: a call argument's expected type was param_node_type_expr re-resolved in the CALLER's scope, so v2.std.text's bare-String parameters read as host text at every foreign call site; it is now the declaration-bound formal (declaration_bound_conformance, then substitution_basis, then declared_type -- the first that is a code-point sequence, formal_code_point_view), so a foreign kernel value unfolds and a foreign literal takes the scalar image. THE ALIASING ESCAPE: a structure-identical local alias (type Wrapped = FreeMonoid, and the renamed type Text) and the local-String alias std.string_type had are refused; a local alias OF THE QUALIFIED declaration (type LocalText = v2.std.text.String) is classified through its qualified target. EVIDENCE: test.claim.text_boundary_identity_wall_witness_test (r_ refusal rows, u_ unfold rows asserting the emitted .chars() route, c_ controls including the roadmap_page script_src_attr join through the real std.markup Attribute), and test.claim.self_host_structural_text_witness_test w_nonliteral_kernel_string_at_text_return_boundary_refuses, the planted restoration probe FLIPPED to a refusal control. POPULATION, MEASURED AND DISPOSITIONED (producer: gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc, base seed vs head seed over the same tree): the wall first raised 87 refusals, every one host text handed to v2.std.text (string_eq 56, string_all_chars_in 15, string_any_char_in 6, string_contains_char 4, other 6). Disposition: string_eq on two host operands rewritten to == in five src/v2/lens modules (the repair this class's RFM row already named); the alphabet-membership and single-string calls unfold at the argument; extdeps.tools.jq's string_join over two host strings rewritten to kernel concat. Final head-vs-base: 0 new blocking rows, 4 removed. THE ROADMAP_PAGE JOIN IS ADMITTED WITH NO EXEMPTION because the crossing no longer exists: 71 modules imported std.string_type { String } -- a structural declaration -- while treating every value as host text, which is a nickname (DESIGN section 3), and std.markup was one of them. All 71 imports are deleted (the kernel String binds; imports never override it). std.string_type's two lexicographic functions, whose bodies were kernel host-text primitives over its structural String, are deleted and their eight callers use host String `<`, which is code-point order (test.claim.emitter_string_order_present_binding_witness_test host_string_order_is_code_point_order_in_the_interpreter; the emitted Rust compares String by the same order). Its `type String = FreeMonoid` STAYS as a declared frontier with no importer (DESIGN section 3c): the open seed kernel-names de-fork ruling retires or promotes it (neat-boar-16's ruling for this change). ONE SEED RULE STATED WITH IT: v1.compiler.infer_env global_bare_lookup never resolves a kernel spelling through the global bare fallback. For String that outcome held only because two modules declared the name, so the entry was ambiguous and nearest-ancestor containment answered nothing; measured on this branch while the duplicate was briefly deleted, a single candidate bound every service-output field spelled String to v2.std.text from anywhere. STANDING STAYS Standing, NARROWED (and restated at the end of this row for the unidentified population), because one crossing is still unguarded: a CALLABLE VALUE whose parameters are code-point sequences passed where a callable over host text is expected (eq: string_eq into a generic contains / dedupe over a List of host strings) -- callable-signature agreement does not consult the text classifier. Lower bound of candidate sites by name: 35 `eq: string_eq` arguments in 20 files, not yet split into host and structural lists. RESTORATION TRIGGER, RESTATED: callable-signature agreement (v1.compiler.infer_types callable_signatures_agree and the generic callable unification feeding it) refuses a code-point-sequence parameter against a host-text one and admits the matching pair, SUFFICIENT FOR every one of those sites to be either admitted as structural-on-structural or refused located; nothing else retires this row. ONE PATH THIS CLIMB DOES NOT REACH, and it is another row's: inside `v2.*` modules the call-argument compat arm does not run (gunbc.rung_drop direct_call_arg_seam_v2_exemption), so a code-point sequence handed to a host-text USER-FUNCTION parameter there is judged only by the inhabitance arm; the unfold of a host value at a code-point parameter is inference and runs everywhere, and the builtin argument check is ungated. A SPELLING COLLISION IS STILL SILENT WRONGNESS, measured 2026-09-28 with compile_dag_diagnostic_census on binaries built at fd0b879 (base) and 314e576 (head). A module declaring its own `type Char { c: Int }` and `type FreeMonoid { items: List }` and handing a host String VALUE to `xs: FreeMonoid` is ADMITTED on both base and head. The container-template machinery recognizes the user record named FreeMonoid by its spelling (v1.compiler.infer_types canonical_template_name over the std.types container-alias rows), so no text judgment runs at all. The text classifier keyed on declaration identity does NOT close this on its own. The capability that does: container-template recognition keyed by the resolved declaration (std.algebra FreeMonoid), SUFFICIENT FOR a user declaration that merely shares the spelling to be judged as the record it is. In the same measurement the classifier this row describes also fabricates: a user `type Char` makes `xs: List` read as a code-point sequence, and a host String value or literal there is unfolded and ADMITTED where base refused. That is why the classifier is being re-keyed on declaration identity before this row's climb lands. OUT OF THIS ROW, recorded where it was found: a local alias of a qualified reference resolves to an unresolved leaf, so `fn a() -> LocalText { 1 }` returns an Int unjudged -- that is not text-specific and belongs to the declared-conformance frontier (declared_type_conformance_note), not here. + +STANDING RESTATED, 2026-09-28 (royal-newt-820; ruling by neat-boar-16 via wise-dove-693). PREVIOUS RUNG for the unidentified population: UNGUARDED -- leaf-name compatibility admitted any value whose spelling matched. TEMPORARY RUNG: IDENTIFIED text crossings are STRUCTURALLY GUARANTEED on the source-acceptance path (refused, located, or unfolded through the declared row). A boundary where one side is identified text and the other has NO readable declaration identity is UNJUDGED: the text wall adds no verdict, and base type compatibility decides exactly as before, never an admission base would not make. Each such site emits the non-blocking diagnostic TextRepresentationUnidentifiedAtBoundary, located. REASON: measured, most v1 type references are unresolved references that carry no declaration identity; refusing them refused 3632 correct sites whole-corpus. POPULATION, BOUNDED BY IDENTITY, PRODUCER NAMED: the rows of diagnostic class TextRepresentationUnidentifiedAtBoundary emitted by `gunbc compile --source-root dag --source-root src/v2 --target dag --repository gunbc --measured-root-demands tools/whole_corpus_compile_measured_root_demands.json` (dag closure) and by `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust` (self-host closure), each row carrying its file:line:col and position. The identity each row reports as unread is v1's ONE type-reference derivation, `v1.compiler.infer_env` `type_reference_declaration_reading` (its `TypeReferenceUnidentified` arm), the same read `v1.tests.claim.carrier_realization_census` records per authored reference position; the census is NOT this population's producer, because it enumerates authored type-reference positions inside declarations, while this population is checker comparisons at use sites whose value side is usually an inferred type with no authored position. RESTORATION TRIGGER, the capability verbatim: the resolver records declaration identity on every type reference; zero unidentified in the self-host and dag closures. STAGED REPLACEMENT: node://adhoc-207dd6ac-6d2 (resolver: declaration identity on every type reference), coordinating with #12540 and deep-bee-18's declaration-reference work. That item is the staged work, not the trigger; the trigger is the capability above. A SECOND GAP NAMED BESIDE C: a user `type Char` record list passed to a host-text builtin (string_length) is admitted on base and head. With identity the list is correctly not text, and the admission is the builtin registry typing its arguments by nothing. The capability that closes it is builtin arguments judged against their declared BuiltinParam types in general, not only for text. + ### Fabric CI evidence lane as a required merge block, and then as a lane at all — declared 2026-08-31 **CORRECTED 2026-09-28: THE CALIBRATION SCRIPT WAS DELETED AND THEN RESTORED, AND THE LANE WAS NOT.** The amendment below said the 2026-09-04 ruling deleted `tools/fabric_ci_evidence_calibration.sh`. #10390 did delete it on 2026-09-04. #10270 (FCI-1) re-added it on 2026-09-05, and it is live: `tools/fabric_ci_fci1_live_instrument.sh` runs it against the exact binary before any srv3 observation. The original sentence is QUOTED, not deleted, because the record of what was claimed is the point: 'together with fabric_ci_evidence_calibration_step, its two derived bounds, and tools/fabric_ci_evidence_calibration.sh'. WHAT THE CORRECTION CHANGES: only the script clause. The script now runs only when an operator invokes the FCI-1 live instrument by hand, on no CI trigger. WHAT IT DOES NOT CHANGE: the `fabric-evidence` job, the calibration step and the FABRIC_EVIDENCE binding are still absent from every emitted workflow. `w_RED_the_deleted_lanes_do_not_return` still guards that absence. A red from a hand-invoked run is still not a merge wall, so the temporary rung (outside the modeled guarantee for merges), the population and the restoration trigger all stand unchanged. The script is its own scaffold with its own dissolution condition in its header; this row does not govern it. **AMENDED 2026-09-04: THE LANE NO LONGER EXECUTES EITHER, AND THIS ROW NOW COVERS BOTH LOSSES.** The 2026-09-04 operator ruling on runner capacity deleted the `fabric-evidence` JOB, together with `fabric_ci_evidence_calibration_step`, its two derived bounds, and `tools/fabric_ci_evidence_calibration.sh`. The declaration below was written when only the merge block was withdrawn and the calibration still ran; read every present-tense claim in it about the lane EXECUTING in the past tense. WHAT CHANGES: the temporary rung falls from mitigatable to outside the modeled guarantee — there is no longer a red for a human to read, because there is no run. WHAT DOES NOT: the restoration trigger, which was already stated at capability grain and is unchanged, and the operational consequence, which now binds harder — the FABRIC-CI program (warm-seal-35) agreed to check `fabric-evidence` explicitly on every head rather than inferring it from `witnesses`, and there is now nothing to check, so any conclusion resting on that lane rests on nothing. THE ROW IS AMENDED RATHER THAN SUPERSEDED because the subject is the same lane and the same trigger retires both losses; a second row would have split one subject across two authorities. The countability moved: `w_RED_fabric_evidence_executes_without_gating` asserted the lane still ran and is replaced by `test.claim.deleted_lanes_countable_witness_test` `w_RED_the_deleted_lanes_do_not_return`, which reds if the job, the FABRIC_EVIDENCE binding or the calibration argv returns while this row stands. The original declaration follows. **THE FABRIC-CI EVIDENCE LANE STOPS BLOCKING MERGES AND KEEPS EXECUTING (2026-08-31).** #9704 added `fabric-evidence` as a fourth job and, in the same change, made it a `needs` of the aggregating `witnesses` job that the `passing CI` ruleset names as this repository's one required context. PREVIOUS RUNG: mechanically preventable — a defect in the FCI-EVIDENCE-0 run-boundary contract was exposed by an enrolled calibration and blocked the merge. TEMPORARY RUNG: mitigatable — the same calibration still runs on every push and pull request, still refuses loudly, and its log is still the record; what is gone is the block, so a red is now a signal a human must read rather than a wall. REASON, and it is wall clock rather than doubt about the evidence: the required context's wall is the MAX over its three lanes, and on run 33350499023 that max was `fabric-evidence` at 27 minutes against 20 for the floor lane and 10.5 for the build lane, having moved the required wall from ~13 minutes to ~28-40 within a day of #9704 landing. 1469 of those 1620 seconds are the calibration step, and its own logs put all ~18 of the plan's `gunbc run` host processes at 80-90 seconds each. That cost is not the evidence: the instrument's import closure is six modules, and per `cli_run.entry_resolve` an `--entry` run is scoped in what it EMITS but WHOLE-TREE in what it PARSES, so each process re-parses and re-censuses the entire 4414-module corpus to evaluate a handful of rows. The lane is paying eighteen whole-corpus censuses (DESIGN §2 duplicated work) for thirteen assertions. Batching them is NOT the repair and is not what this row defers: the calibration-timeout derivation recorded that distinct host processes ARE the process-edge subject (that row was deleted with the lane on 2026-09-04; its reasoning is preserved in this sentence), so fewer processes would remove the boundary rather than observe it more cheaply. BOUNDED POPULATION: exactly one job, `fabric-evidence`, and exactly one lost edge — its `needs.fabric-evidence.result` binding into the aggregate's closed-vocabulary verdict fold. No other lane, phase, witness or claim changes rung; the build and floor lanes keep every edge they had, and the calibration keeps every assertion it had. The drop is countable from the emitted workflow: `test.claim.witness_floor_workflow_consolidation_witness_test` `w_RED_fabric_evidence_executes_without_gating` reds if the job stops executing, and reds again if either gating spelling returns while this row still stands. ONE OPERATIONAL CONSEQUENCE, recorded here because it is the kind of practice that quietly stops once the person who agreed to it moves on. With the needs edge withdrawn, a `fabric-evidence` failure no longer reaches the aggregate verdict, so any lane that was reading the required context as a proxy for that lane's health is now reading a context that cannot see it. The FABRIC-CI program (warm-seal-35) accepted the drop on that basis and will check `fabric-evidence` EXPLICITLY on every head rather than inferring it from `witnesses`. Anyone else joining on the required context owes the same explicit check for as long as this row stands; that obligation ends when the trigger below is met and not before. RESTORATION TRIGGER: the CAPABILITY of a single fabric evidence process resolving its entry against the source roots at a cost that fits the required wall budget — concretely, the whole-tree parse and name census being paid once per tree state rather than once per host process, so that the 13-row plan executes its full process-edge roster inside the lane's share of a ~10-minute required wall. THIS IS NOT RETIRED BY THE MEMO PR MERGING. It is retired by a measured `fabric-evidence` job wall that leaves the required context at or under its budget with the needs edge and the FABRIC_EVIDENCE binding restored, which is the only thing that makes the block affordable again. Reducing the plan's process count, relaxing what a row asserts, or moving the calibration to a cadence does not satisfy this trigger — the first removes the subject, and the other two are different drops that would need their own rows. From 455dcd8f2105ef851f6c88b001549b4f178b7ce0 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 2 Oct 2026 10:20:38 +0000 Subject: [PATCH 23/24] Annotations: an unidentified text side is UNJUDGED by the wall, not refused (review 74123) std.coercion TextRepresentationUnidentified and 04_infer's unfold note claimed a refusal the code does not perform: text_representations_cross answers false for an unidentified side and text_unjudged_advisories emits only the advisory, so base compatibility decides. Restated at the true standing, bounded by gunbc.rung_drop text_boundary_identity_wall and its trigger. Annotation-only (DESIGN 4c): no semantic or generated-byte change. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/std/coercion.dag | 8 +++++++- src/v1/04_infer.dag | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dag/std/coercion.dag b/dag/std/coercion.dag index 92598e501fc..e9b47d71e6b 100644 --- a/dag/std/coercion.dag +++ b/dag/std/coercion.dag @@ -122,7 +122,13 @@ type TextRepresentation = HostText | CodePointSequence | NotText | TextRepresent // TextRepresentationUnidentified: the node's declaration could not be read by identity (no declaring // span in the global index, no resolvable qualified name, an alias chain that cycles or exceeds its -// bound). It is never read as NotText: a text boundary refuses a value it cannot identify. +// bound). It is never read as NotText, and it is not refused either: a boundary where one side is +// unidentified is UNJUDGED by the text wall. text_representations_cross answers false for it, the +// wall adds no verdict, base type compatibility decides exactly as before, and the site emits only +// the non-blocking TextRepresentationUnidentifiedAtBoundary advisory, located. That population is +// the declared drop gunbc.rung_drop text_boundary_identity_wall, whose restoration trigger is the +// capability: the resolver records declaration identity on every type reference; zero unidentified +// in the self-host and dag closures. // WHICH STATE A TYPE REFERENCE IS IN WHEN ITS DECLARATION IS ASKED FOR -- the split of the one arm // the reference-site read used to answer for two states. v1.std.core type_reference_provenance diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index bd64a141308..539728c1cf8 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -7447,8 +7447,10 @@ fn expression_is_uppercase_constructor_reference(n: Node, source_indices: Map Bool { match literal_boundary_elaboration(lit: LitStr { value: "" }, expected: Present { value: destination_type }, scope: scope) { LiteralBoundaryElaborated { elaboration: e, destination_type: _ } => From bd5bf21736426abf3b2f981e538324aa25318a5d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 2 Oct 2026 18:13:11 +0000 Subject: [PATCH 24/24] =?UTF-8?q?Regenerate=20stage0=20infer=20mirrors=20t?= =?UTF-8?q?o=20the=20fixed=20point=20after=20the=20main=20merge=20(claim?= =?UTF-8?q?=5Fexecutor=20--required-regen,=20claim=5Fexecutor=20sha256=20c?= =?UTF-8?q?6d38a43=E2=80=A6,=20rounds=201=3D=3D2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/stage0/src/v1_compiler_infer.rs | 589 +++++++++++++++--- src/v1/stage0/src/v1_compiler_infer_method.rs | 62 +- 2 files changed, 552 insertions(+), 99 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 0989da31d00..47d1c5cb854 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -19,11 +19,16 @@ use crate::std_algebra::CollectionSizeEffect::ShrinkEffect; pub use crate::std_algebra::{carrier_container_equality_rows, kernel_carrier_admits_numeral}; pub use crate::std_algebra::{CollectionSizeEffect, FreeMonoid}; use crate::std_coercion::SubtractionRefinement::SubtractionRefinementAmbiguous; +use crate::std_coercion::TextRepresentation::{ + CodePointSequence, HostText, NotText, TextRepresentationUnidentified, +}; use crate::std_coercion::TypeDeclarationProvenance::{ CorpusDeclared, DeclarationIdentityAbsent, KernelMinted, }; pub use crate::std_coercion::{dag_can_cast, dag_cast_requires_proof, is_dag_cast_domain_type}; -pub use crate::std_coercion::{SubtractionRefinement, TypeDeclarationProvenance}; +pub use crate::std_coercion::{ + SubtractionRefinement, TextRepresentation, TypeDeclarationProvenance, +}; pub use crate::std_computation::ShrinkFactor; use crate::std_computation::ShrinkFactor::{ConstantShrink, ProportionalShrink, UnitShrink}; use crate::std_content_hash::ContentHash::*; @@ -116,7 +121,9 @@ pub use crate::v1_compiler_infer_env::{ qualified_all_but_last, qualify_borrowed_inferred, qualify_borrowed_type_names, qualify_decl_reference_positions, str_bindings_from_bindings, symbol_index_insert, symbol_index_insert_decl, symbol_index_insert_service, symbol_index_lookup, - type_reference_declaration, type_reference_declaration_ref, unit_variant_index_shadow_insert, + text_crossing_by_identity, text_representation_by_identity, text_representation_is_text_arm, + text_representation_is_unidentified, text_representations_cross, type_reference_declaration, + type_reference_declaration_ref, unit_variant_index_shadow_insert, }; pub use crate::v1_compiler_infer_env::{ GlobalBareCandidate, GlobalBareLookupState, GuardedTypeEnvCacheMerge, ServiceCensusEntry, @@ -157,7 +164,8 @@ pub use crate::v1_compiler_infer_lookup::{ ExactDeclarationIdentity, KnownMethodResolution, }; pub use crate::v1_compiler_infer_method::{ - builtin_kernel_seed_diagnostics, infer_builtin_call_type, resolve_builtin_call_type, + builtin_host_text_param_names, builtin_kernel_seed_diagnostics, builtin_param_names, + infer_builtin_call_type, resolve_builtin_call_type, }; use crate::v1_compiler_infer_patterns::PatternSubject::*; pub use crate::v1_compiler_infer_patterns::{ @@ -206,7 +214,10 @@ pub use crate::v1_compiler_infer_sigs::{ CallableCandidate, CallableIdentity, DerivedCalleeSig, FuncSigLookup, NoDerivableSigReason, ResolveFuncSigsResult, ResolvedFormals, ResolvedFuncEnv, ResolvedFuncSig, }; -pub use crate::v1_compiler_infer_types::KernelTypeBuild; +use crate::v1_compiler_infer_types::TextJudgment::{TextJudgedIn, TextNotAsked}; +use crate::v1_compiler_infer_types::TextNotAskedReason::{ + TextNotAskedForCallableComponentResidue, TextNotAskedInVariantFieldSummary, +}; pub use crate::v1_compiler_infer_types::{ bare_map_node, bare_set_node, callable_inferred, callable_return_type, child_type_node, emit_map_has, extract_optional_inner_node, for_each_element_type_node, infer_binop_type_node, @@ -219,6 +230,7 @@ pub use crate::v1_compiler_infer_types::{ structural_carrier_template_name, subtraction_refinement_of, template_return_has_variables, template_return_is_receiver_self, }; +pub use crate::v1_compiler_infer_types::{KernelTypeBuild, TextJudgment, TextNotAskedReason}; pub use crate::v1_compiler_resolve::{ModuleGraph, ResolvedImport, ResolvedModule}; use crate::v1_compiler_type_head_exposure::TypeHeadExposure::{ ExposedTypeHead, MalformedApplicationHead, OpaqueTypeHead, StuckTypeHead, @@ -250,9 +262,9 @@ use crate::v1_std_core::CompilerDiagnostic::{ FrontierOccurrenceBudgetExceeded, InternalError, MethodExistenceFrontierAdmitted, MethodExistenceUndecided, MethodNotFound, MissingField, OptionalCastNotEliminated, ReceiverTypeUnestablished, ServiceConfigReferenceJudgmentDeferred, - SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, TypeArgumentArityMismatch, - TypeMismatch, TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, - VariantCollision, + SiblingOperandEffectOrderUndetermined, SoleConstructorViolation, + TextRepresentationUnidentifiedAtBoundary, TypeArgumentArityMismatch, TypeMismatch, + TypeParameterInValuePosition, UnlistedVariantValueUse, UnresolvedType, VariantCollision, }; use crate::v1_std_core::Connective::{Arrow, Conj, Disj, NoConnective}; use crate::v1_std_core::DeclarationMarker::Unmarked; @@ -2080,6 +2092,41 @@ pub fn declared_type_conformance_diags( produced_by: Rc, span: Rc, scope: Rc, +) -> Rc>> { + v1_rt::concat( + text_unjudged_advisories( + crate::v1_compiler_infer_env::text_representation_by_identity( + declared.clone(), + scope.type_env.clone(), + ), + crate::v1_compiler_infer_env::text_representation_by_identity( + produced.clone(), + scope.type_env.clone(), + ), + "a declared type".to_string(), + span.clone(), + scope.module_name.clone(), + ), + declared_type_conformance_diags_core( + position.clone(), + subject.clone(), + declared.clone(), + produced.clone(), + produced_by.clone(), + span.clone(), + scope.clone(), + ), + ) +} + +pub fn declared_type_conformance_diags_core( + position: DeclaredTypePosition, + subject: String, + declared: Rc, + produced: Rc, + produced_by: Rc, + span: Rc, + scope: Rc, ) -> Rc>> { { let si = scope.type_env.clone().source_indices.clone(); @@ -2099,10 +2146,10 @@ pub fn declared_type_conformance_diags( if ((cardinality_diags.clone().len() as i64) > 0) { cardinality_diags.clone() } else { - if declared_type_kernel_inhabitance_mismatch_here_or_at_element( + if crate::v1_compiler_infer_env::text_crossing_by_identity( declared.clone(), produced.clone(), - scope.clone(), + scope.type_env.clone(), ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape(declared.clone(), si.clone()), @@ -2111,12 +2158,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if (nominal_product_inhabitance_refusal( + if declared_type_kernel_inhabitance_mismatch_here_or_at_element( declared.clone(), produced.clone(), scope.clone(), - ) != std::option::Option::None) - { + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2130,11 +2176,12 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if nominal_coproduct_applied_argument_conflict( + if (nominal_product_inhabitance_refusal( declared.clone(), produced.clone(), scope.clone(), - ) { + ) != std::option::Option::None) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2148,7 +2195,7 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if coproduct_payload_where_parent_required( + if nominal_coproduct_applied_argument_conflict( declared.clone(), produced.clone(), scope.clone(), @@ -2166,14 +2213,11 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if ((type_node_is_arrow(declared.clone()) - && type_node_is_arrow(produced.clone())) - && callable_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - )) - { + if coproduct_payload_where_parent_required( + declared.clone(), + produced.clone(), + scope.clone(), + ) { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2187,11 +2231,14 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if callable_element_signature_mismatch( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if ((type_node_is_arrow(declared.clone()) + && type_node_is_arrow(produced.clone())) + && callable_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + )) + { Rc::new(vec![type_mismatch_error( crate::v1_compiler_infer_types::node_type_shape( declared.clone(), @@ -2205,28 +2252,50 @@ pub fn declared_type_conformance_diags( scope.module_name.clone(), )]) } else { - if !both_ground.clone() { - Rc::new(vec![]) + if callable_element_signature_mismatch( + declared.clone(), + produced.clone(), + si.clone(), + ) { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) } else { - if crate::v1_compiler_infer_types::node_type_compatible( - declared.clone(), - produced.clone(), - si.clone(), - ) { + if !both_ground.clone() { Rc::new(vec![]) } else { - Rc::new(vec![type_mismatch_error( - crate::v1_compiler_infer_types::node_type_shape( - declared.clone(), - si.clone(), - ), - crate::v1_compiler_infer_types::node_type_shape( - produced.clone(), - si.clone(), - ), - span.clone(), - scope.module_name.clone(), - )]) + if crate::v1_compiler_infer_types::node_type_compatible( + declared.clone(), + produced.clone(), + si.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), + ) { + Rc::new(vec![]) + } else { + Rc::new(vec![type_mismatch_error( + crate::v1_compiler_infer_types::node_type_shape( + declared.clone(), + si.clone(), + ), + crate::v1_compiler_infer_types::node_type_shape( + produced.clone(), + si.clone(), + ), + span.clone(), + scope.module_name.clone(), + )]) + } } } } @@ -2812,6 +2881,7 @@ pub fn set_element_types_mismatch( recv_elem: Rc, operand_elem: Rc, source_indices: Rc>>, + text: Rc, ) -> bool { ((set_element_type_is_concrete(recv_elem.clone()) && set_element_type_is_concrete(operand_elem.clone())) @@ -2819,6 +2889,7 @@ pub fn set_element_types_mismatch( recv_elem.clone(), operand_elem.clone(), source_indices.clone(), + text.clone(), )) } @@ -3972,6 +4043,9 @@ pub fn match_arm_types_are_proven_disjoint( unified_arm_type.clone(), arm_type.clone(), source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) == false)) } } @@ -8160,6 +8234,9 @@ pub fn callable_component_ground_mismatch( formal_part.clone(), instantiated.clone(), source_indices.clone(), + Rc::new(TextJudgment::TextNotAsked { + reason: TextNotAskedReason::TextNotAskedForCallableComponentResidue, + }), ) } else { false @@ -8421,12 +8498,16 @@ pub fn direct_call_arg_type_mismatch( { false } else { - ((nominal_call_arg_brand_mismatch( + (((crate::v1_compiler_infer_env::text_crossing_by_identity( + formal.clone(), + actual.clone(), + type_env.clone(), + ) || nominal_call_arg_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), source_indices.clone(), - ) || container_element_nominal_brand_mismatch( + )) || container_element_nominal_brand_mismatch( formal.clone(), actual.clone(), type_env.clone(), @@ -9097,6 +9178,49 @@ pub fn direct_call_shape_wall_note() -> String { CACHED.with(|c: &String| c.clone()) } +pub fn argument_text_representation(value: Rc, type_env: Rc) -> TextRepresentation { + crate::v1_compiler_infer_env::text_representation_by_identity( + expression_value_type(value.clone()), + type_env.clone(), + ) +} + +pub fn text_unjudged_advisories( + a: TextRepresentation, + b: TextRepresentation, + position: String, + span: Rc, + module_name: String, +) -> Rc>> { + if ((crate::v1_compiler_infer_env::text_representation_is_text_arm(a.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(b.clone())) + || (crate::v1_compiler_infer_env::text_representation_is_text_arm(b.clone()) + && crate::v1_compiler_infer_env::text_representation_is_unidentified(a.clone()))) + { + Rc::new(vec![crate::v1_std_core::make_error_node( + Rc::new( + CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: position.clone(), + span: span.clone(), + }, + ), + module_name.clone(), + )]) + } else { + Rc::new(vec![]) + } +} + +pub fn text_representations_both_code_point(a: TextRepresentation, b: TextRepresentation) -> bool { + match a.clone() { + TextRepresentation::CodePointSequence => match b.clone() { + TextRepresentation::CodePointSequence => true, + _ => false, + }, + _ => false, + } +} + pub fn direct_call_arg_mismatch_diags( plan: Rc>>, typed_args: Rc>>, @@ -9123,11 +9247,29 @@ if match (*actual_expr.expr_data.clone()).clone() { { let actual_raw = expression_value_type(actual_expr.clone()); let actual = crate::v1_compiler_infer_resolve::peel_nominal_alias_identity(actual_raw.clone(), type_env.clone(), module_name.clone()); -if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { - Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) +let formal_text = if (formal_code_point_view(app.formal.clone(), type_env.clone()) != std::option::Option::None) { + TextRepresentation::CodePointSequence } else { - Rc::new(vec![]) - } + crate::v1_compiler_infer_env::text_representation_by_identity(formal.clone(), type_env.clone()) + }; +let actual_text = argument_text_representation(actual_expr.clone(), type_env.clone()); +let unjudged = text_unjudged_advisories(formal_text.clone(), actual_text.clone(), "a call argument".to_string(), actual_expr.span.clone(), module_name.clone()); +v1_rt::concat(unjudged.clone(), if crate::v1_compiler_infer_env::text_representations_cross(formal_text.clone(), actual_text.clone()) { + Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::InternalError { + message: v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing at a call argument: declared ".to_string(), crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone())), ", produced ".to_string()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone())), " -- host text and a code-point sequence (FreeMonoid) meet only through the Unicode scalar unfold, which is applied to a host value at a code-point formal; a code-point sequence has no declared route into host text".to_string()), + span: actual_expr.span.clone(), +}), module_name.clone())]) + } else { + if text_representations_both_code_point(formal_text.clone(), actual_text.clone()) { + Rc::new(vec![]) + } else { + if direct_call_arg_type_mismatch(formal.clone(), app.formal.clone().substitution_basis.clone(), actual.clone(), actual_expr.clone(), type_env.clone(), module_name.clone(), source_indices.clone()) { + Rc::new(vec![type_mismatch_error(crate::v1_compiler_infer_types::node_type_shape(formal.clone(), source_indices.clone()), crate::v1_compiler_infer_types::node_type_shape(actual.clone(), source_indices.clone()), actual_expr.span.clone(), module_name.clone())]) + } else { + Rc::new(vec![]) + } + } + }) } } }, @@ -9148,6 +9290,71 @@ pub struct Tier2bBt { pub kernel_diags: Rc>>, } +pub fn builtin_text_argument_crossing_diags( + func_name: String, + typed_args: Rc>>, + scope: Rc, +) -> Rc>> { + { + let host_params = + crate::v1_compiler_infer_method::builtin_host_text_param_names(func_name.clone()); + if ((host_params.clone().len() as i64) == 0) { + Rc::new(vec![]) + } else { + { + let formal_names = + crate::v1_compiler_infer_method::builtin_param_names(func_name.clone()); + Rc::new({ + let mut __result = Vec::new(); + for pair in Rc::new( + typed_args + .clone() + .iter() + .cloned() + .enumerate() + .map(|(i, v)| (i as i64, v)) + .collect::>(), + ) + .iter() + .cloned() + { + __result.extend((*{ + let arg = pair.1.clone(); +let bound_name = match crate::v1_std_core::arg_name_at(arg.clone(), scope.type_env.clone().source_indices.clone()) { + Some(label) => label.clone(), + std::option::Option::None => match formal_names.clone().iter().cloned().skip(pair.0.clone() as usize).next() { + Some(n) => n.clone(), + std::option::Option::None => "".to_string(), +}, +}; +if !{ let mut __found = false; for n in host_params.iter().cloned() { if (n.clone() == bound_name.clone()) { __found = true; break; } } __found } { + Rc::new(vec![]) + } else { + { + let value = crate::v1_std_core::arg_value(arg.clone()); +match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => match argument_text_representation(value.clone(), scope.type_env.clone()) { + TextRepresentation::CodePointSequence => Rc::new(vec![inference_error(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("text representation crossing: argument '".to_string(), bound_name.clone()), "' of ".to_string()), func_name.clone()), " is host text, and the value passed is a code-point sequence (FreeMonoid); no declared route converts a non-literal between the two -- pass a host String, or use the structural operation on the sequence".to_string()), value.span.clone(), scope.module_name.clone())]), + TextRepresentation::HostText => Rc::new(vec![]), + TextRepresentation::NotText => Rc::new(vec![]), + TextRepresentation::TextRepresentationUnidentified => Rc::new(vec![crate::v1_std_core::make_error_node(Rc::new(CompilerDiagnostic::TextRepresentationUnidentifiedAtBoundary { + position: v1_rt::concat("argument '".to_string(), v1_rt::concat(bound_name.clone(), v1_rt::concat("' of ".to_string(), func_name.clone()))), + span: value.span.clone(), +}), scope.module_name.clone())]), +}, + _ => Rc::new(vec![]), +} +} + } +}).iter().cloned()); + } + __result + }) + } + } + } +} + pub fn infer_tier2b_builtin_with_kernel_diags( func_name: String, typed_args: Rc>>, @@ -9404,6 +9611,9 @@ pub fn infer_tier2b_builtin_with_kernel_diags( re.clone(), oe.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ), std::option::Option::None => false, }, @@ -11066,6 +11276,106 @@ pub fn expression_is_uppercase_constructor_reference( } } +pub fn destination_declares_scalar_sequence_unfold( + destination_type: Rc, + scope: Rc, +) -> bool { + match (*literal_boundary_elaboration( + Rc::new(LiteralValue::LitStr { + value: "".to_string(), + }), + Some(destination_type.clone()), + scope.clone(), + )) + .clone() + { + LiteralBoundary::LiteralBoundaryElaborated { elaboration: e, .. } => { + match (*e.homomorphism.clone().producer.clone()).clone() { + LiteralUnfolding::UnicodeScalarSequenceUnfold => true, + _ => false, + } + } + LiteralBoundary::LiteralBoundaryPlain => false, + LiteralBoundary::LiteralBoundaryRefused { message: _, .. } => false, + } +} + +pub fn host_text_unfolded_at_code_point_boundary( + value: Rc, + destination_type: Rc, + scope: Rc, +) -> Rc { + match value.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: _, .. }) => { + match argument_text_representation(value.clone(), scope.type_env.clone()) { + TextRepresentation::HostText => { + if destination_declares_scalar_sequence_unfold( + destination_type.clone(), + scope.clone(), + ) { + crate::v1_std_core::make_named_expr_node( + Rc::new(NodeOccurrenceIdentity::OccurrenceSynthetic), + "chars".to_string(), + Rc::new(ExprData::ExprMethodCall { + method_semantics: Some(Rc::new( + MethodSemantics::PlainMethodSemantics, + )), + }), + Rc::new(vec![value.clone()]), + Some(Rc::new(InferredNode::Resolved { + node: destination_type.clone(), + })), + value.span.clone(), + elaborated_span("chars".to_string()), + ) + } else { + value.clone() + } + } + TextRepresentation::CodePointSequence => value.clone(), + TextRepresentation::NotText => value.clone(), + TextRepresentation::TextRepresentationUnidentified => value.clone(), + } + } + _ => value.clone(), + } +} + +pub fn formal_code_point_view(formal: Rc, env: Rc) -> Option> { + match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declared_type.clone(), + env.clone(), + ) { + TextRepresentation::HostText => std::option::Option::None, + _ => formal_code_point_view_peeled(formal.clone(), env.clone()), + } +} + +pub fn formal_code_point_view_peeled( + formal: Rc, + env: Rc, +) -> Option> { + match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declaration_bound_conformance.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declaration_bound_conformance.clone()), + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.substitution_basis.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.substitution_basis.clone()), + _ => match crate::v1_compiler_infer_env::text_representation_by_identity( + formal.declared_type.clone(), + env.clone(), + ) { + TextRepresentation::CodePointSequence => Some(formal.declared_type.clone()), + _ => std::option::Option::None, + }, + }, + } +} + pub fn infer_call_arguments_generic_pass( call_args: Rc>>, value_params: Rc>>, @@ -11154,33 +11464,79 @@ pub fn infer_call_arguments_generic_pass( type_variable_node("callable_param".to_string()) } }; - let contextual_expected = + let formal_declaration_bound = match (*formal_selection.clone()).clone() { + CallArgumentFormalSelection::CallArgumentFormalSelected { + formal_index, .. + } => match formals + .clone() + .iter() + .cloned() + .skip(formal_index.clone() as usize) + .next() + { + Some(carried) => { + formal_code_point_view(carried.clone(), scope.type_env.clone()) + } + std::option::Option::None => std::option::Option::None, + }, + CallArgumentFormalSelection::CallArgumentFormalUnavailable => { + std::option::Option::None + } + }; + let formal_is_code_point_sequence = + (formal_declaration_bound.clone() != std::option::Option::None); + let code_point_formal = match formal_declaration_bound.clone() { + Some(bound) => bound.clone(), + std::option::Option::None => formal_conformance_type.clone(), + }; + let argument_is_literal = match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { - ExprData::ExprVar { - binding_kind: _, .. - } => { - if expression_is_uppercase_constructor_reference( - crate::v1_std_core::arg_value(a.clone()), - scope.type_env.clone().source_indices.clone(), - ) { - formal_conformance_type.clone() - } else { - formal_param_type.clone() + ExprData::ExprLiteral { value: _, .. } => true, + _ => false, + }; + let contextual_expected = + if (formal_is_code_point_sequence.clone() && argument_is_literal.clone()) { + code_point_formal.clone() + } else { + match (*crate::v1_std_core::arg_value(a.clone()).expr_data.clone()).clone() { + ExprData::ExprVar { + binding_kind: _, .. + } => { + if expression_is_uppercase_constructor_reference( + crate::v1_std_core::arg_value(a.clone()), + scope.type_env.clone().source_indices.clone(), + ) { + formal_conformance_type.clone() + } else { + formal_param_type.clone() + } } + ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), + _ => formal_param_type.clone(), } - ExprData::ExprRecordLit { parent_enum: _, .. } => formal_param_type.clone(), - _ => formal_param_type.clone(), }; let expected = if has_formal.clone() { Some(contextual_expected.clone()) } else { std::option::Option::None }; - let ar = infer_expr( + let ar_inferred = infer_expr( crate::v1_std_core::arg_value(a.clone()), scope.clone(), expected.clone(), ); + let ar = if formal_is_code_point_sequence.clone() { + Rc::new(InferResult { + typed: host_text_unfolded_at_code_point_boundary( + ar_inferred.typed.clone(), + code_point_formal.clone(), + scope.clone(), + ), + diagnostics: ar_inferred.diagnostics.clone(), + }) + } else { + ar_inferred.clone() + }; let next_subst = if (is_lambda_expr(crate::v1_std_core::arg_value(a.clone())) || !has_formal.clone()) { @@ -11428,6 +11784,10 @@ pub enum LiteralBoundary { }, } +pub fn code_point_sequence_structure_ref() -> Rc { + crate::std_decl_ref::decl_ref("std.algebra".to_string(), "FreeMonoid".to_string()) +} + pub fn literal_boundary_elaboration( lit: Rc, expected: Option>, @@ -11439,36 +11799,57 @@ pub fn literal_boundary_elaboration( if (exp.return_cardinality.clone() == Cardinality::CardOptional) { Rc::new(LiteralBoundary::LiteralBoundaryPlain) } else { - match crate::v1_compiler_infer_env::type_reference_declaration_ref( - exp.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) { - std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), - Some(destination) => { - let kind = - crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); - let element = if ((exp.children.clone().len() as i64) == 1) { - match exp.children.clone().first().cloned() { - Some(arg) => { - crate::v1_compiler_infer_env::type_reference_declaration_ref( - arg.clone(), - scope.type_env.clone().source_indices.clone(), - scope.type_env.clone(), - ) - } - std::option::Option::None => std::option::Option::None, - } - } else { - std::option::Option::None + { + let is_code_point_sequence = + match crate::v1_compiler_infer_env::text_representation_by_identity( + exp.clone(), + scope.type_env.clone(), + ) { + TextRepresentation::CodePointSequence => true, + TextRepresentation::HostText => false, + TextRepresentation::NotText => false, + TextRepresentation::TextRepresentationUnidentified => false, }; - let natively = crate::v1_compiler_coercion::provenance_realizes_natively( - crate::v1_compiler_infer_env::declaration_provenance_of_ref( - destination.clone(), - scope.type_env.clone(), - ), - ); - match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { + let identified = if is_code_point_sequence.clone() { + Some(code_point_sequence_structure_ref()) + } else { + crate::v1_compiler_infer_env::type_reference_declaration_ref( + exp.clone(), + scope.type_env.clone().source_indices.clone(), + scope.type_env.clone(), + ) + }; + match identified.clone() { + std::option::Option::None => Rc::new(LiteralBoundary::LiteralBoundaryPlain), + Some(destination) => { + let kind = + crate::std_literal_elaboration::literal_source_kind_of(lit.clone()); + let element = if is_code_point_sequence.clone() { + Some(crate::std_decl_ref::decl_ref( + "std.types".to_string(), + "Char".to_string(), + )) + } else { + if ((exp.children.clone().len() as i64) == 1) { + match exp.children.clone().first().cloned() { + Some(arg) => crate::v1_compiler_infer_env::type_reference_declaration_ref(arg.clone(), scope.type_env.clone().source_indices.clone(), scope.type_env.clone()), + std::option::Option::None => std::option::Option::None, +} + } else { + std::option::Option::None + } + }; + let natively = if is_code_point_sequence.clone() { + false + } else { + crate::v1_compiler_coercion::provenance_realizes_natively( + crate::v1_compiler_infer_env::declaration_provenance_of_ref( + destination.clone(), + scope.type_env.clone(), + ), + ) + }; + match (*crate::std_literal_elaboration::elaborate_literal_at(literal_homomorphism_rows(), kind.clone(), destination.clone(), element.clone(), natively.clone())).clone() { LiteralElaborationOutcome::DirectLiteral => Rc::new(LiteralBoundary::LiteralBoundaryPlain), LiteralElaborationOutcome::ViaHomomorphism { homomorphism: h, .. } => Rc::new(LiteralBoundary::LiteralBoundaryElaborated { elaboration: Rc::new(LiteralElaboration { @@ -11482,6 +11863,7 @@ pub fn literal_boundary_elaboration( message: crate::std_literal_elaboration::literal_elaboration_refusal_message(c.clone()), }), } + } } } } @@ -13109,7 +13491,7 @@ Rc::new(InferResult { }), typed_arg_nodes.clone(), Some(Rc::new(InferredNode::Resolved { node: bt.clone(), })), span.clone(), crate::v1_std_core::node_name_span(texpr.clone())), - diagnostics: v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), + diagnostics: v1_rt::concat(v1_rt::concat(arg_diags.clone(), tier2b.kernel_diags.clone()), builtin_text_argument_crossing_diags(func_name.clone(), typed_args.clone(), scope.clone())), }) } } else { @@ -13951,6 +14333,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), then_rt.clone(), else_rt.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), ) { Rc::new(vec![]) } else { @@ -14920,6 +15305,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )), _ => std::option::Option::None, }, @@ -14987,6 +15375,9 @@ crate::v1_compiler_infer_types::resolve_type_variables_from_template(t.clone(), span.clone(), scope.module_name.clone(), scope.type_env.clone().source_indices.clone(), + Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), + }), )) } _ => std::option::Option::None, @@ -16753,7 +17144,9 @@ if direct_call_formal_has_unbound_type_variable(field_conformance_type.clone()) if ((field_cardinality_diags.clone().len() as i64) > 0) { field_cardinality_diags.clone() } else { - if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone()) { + if crate::v1_compiler_infer_types::node_type_compatible(expected_node.clone(), got_node.clone(), scope.type_env.clone().source_indices.clone(), Rc::new(TextJudgment::TextJudgedIn { + env: scope.type_env.clone(), +})) { Rc::new(vec![]) } else { if declared_alias_target_matches_produced(expected_node.clone(), got_node.clone(), scope.type_env.clone()) { diff --git a/src/v1/stage0/src/v1_compiler_infer_method.rs b/src/v1/stage0/src/v1_compiler_infer_method.rs index 47a59ee929b..c605409a947 100644 --- a/src/v1/stage0/src/v1_compiler_infer_method.rs +++ b/src/v1/stage0/src/v1_compiler_infer_method.rs @@ -7,7 +7,9 @@ use crate::std_algebra::AlgebraTypeTemplate::{ ReceiverKey, ReceiverSelf, ReceiverValue, TupleOf, WitnessOf, }; use crate::std_algebra::ContainerSource::{Named, SameAsReceiver}; -pub use crate::std_algebra::{algebra_templates_for_profile, all_algebra_profiles}; +pub use crate::std_algebra::{ + algebra_templates_for_profile, all_algebra_profiles, free_monoid_scalar_templates, +}; pub use crate::std_algebra::{ AlgebraFieldTemplate, AlgebraProfile, AlgebraTypeTemplate, ContainerSource, }; @@ -2060,6 +2062,64 @@ pub fn builtin_signature(name: String) -> Option> { v1_rt::map_get(&builtin_function_registry(), name.clone()) } +pub fn builtin_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + Some(sig) => Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + __result.push(p.name.clone()); + } + __result + }), + std::option::Option::None => Rc::new(vec![]), + } +} + +pub fn builtin_host_text_param_names(name: String) -> Rc> { + match builtin_signature(name.clone()) { + std::option::Option::None => Rc::new(vec![]), + Some(sig) => { + let scalar_string_method = { + let mut __found = false; + for t in crate::std_algebra::free_monoid_scalar_templates() + .iter() + .cloned() + { + if (t.name.clone() == name.clone()) { + __found = true; + break; + } + } + __found + }; + Rc::new({ + let mut __result = Vec::new(); + for p in Rc::new({ + let mut __result = Vec::new(); + for p in sig.params.clone().iter().cloned() { + if match (*p.ty.clone()).clone() { + AlgebraTypeTemplate::NamedTemplate { name: n, .. } => { + (n.clone() == "String".to_string()) + } + AlgebraTypeTemplate::ReceiverSelf => scalar_string_method.clone(), + _ => false, + } { + __result.push(p); + } + } + __result + }) + .iter() + .cloned() + { + __result.push(p.name.clone()); + } + __result + }) + } + } +} + pub fn infer_builtin_call_type(name: String) -> Option> { match builtin_signature(name.clone()) { Some(sig) => Some(sig.returns.clone()),