From 39a6503c2a00a0c93b4b56f58ab291ec0a835e93 Mon Sep 17 00:00:00 2001 From: Eric Stern Date: Fri, 14 Aug 2026 11:57:14 -0700 Subject: [PATCH 1/3] Settle the constant type as one shape, not two --- docs/architecture/0002-execution-plan.md | 9 +++++++++ docs/architecture/build-manifest.md | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/architecture/0002-execution-plan.md b/docs/architecture/0002-execution-plan.md index cc4d3a3..643d698 100644 --- a/docs/architecture/0002-execution-plan.md +++ b/docs/architecture/0002-execution-plan.md @@ -647,6 +647,15 @@ To stop the two typing models fighting before they are built: kind is implicit and `NameKind` is not passed. `ClassLikeName` is today's `ClassName` (which per CLAUDE.md also serves as the class `Type`); whether it is reused as-is, renamed, or wrapped is an open decision (§7). The other three are new. +- **One constant type, class-declared or not.** `Domain\ConstantInfo` serves both: + `declaringClass` is `?ClassName`, and its absence is what makes a constant global. A + global constant is mechanically public and final, so `visibility` and `isFinal` carry + the true value rather than a placeholder, and `format()` branches on the declaring + class alone. `Domain\ConstantName` likewise holds either a member name or an FQN, so + the `ConstantName` above needs no second type. A global constant is **not** a + `ResolvedMember` — that interface means *reached through a class*, which is a path it + does not have — so `ConstantInfo` implements `ResolvedSymbol` itself rather than + gaining a fifth wrapper, which is also a down payment on the collapse in #416. - `locate(QualifiedName, NameKind)` is the kind-agnostic entry, used when the caller has an FQN whose kind is known only from syntactic position and has not minted a typed subtype. `NameKind` is **not** redundant here precisely because the input is diff --git a/docs/architecture/build-manifest.md b/docs/architecture/build-manifest.md index c8b3f8e..3c6706e 100644 --- a/docs/architecture/build-manifest.md +++ b/docs/architecture/build-manifest.md @@ -176,10 +176,11 @@ Notes: Step 2 carries `ClassLikeName` / `NamespaceName`; `QualifiedName` lands in **S3.7b**, whose `DeclarationScanner` is its first caller; `FunctionName` in **S3.8a** and `ConstantName` in **S3.8b**, with their lookups. - - **`ConstantName` is already taken.** `Domain\ConstantName` wraps a *class* constant - name; §5.3's `ConstantName` is a *global* constant FQN. Decide the naming before - S3.8b rather than inside it — this is the same coexistence question §7 leaves open - for `ClassLikeName` versus `ClassName`. + - **`ConstantName` is already taken, and stays one type.** `Domain\ConstantName` wraps + a *class* constant name; §5.3's `ConstantName` is a *global* constant FQN. One type + holds both, as does one `ConstantInfo`: a global constant is a constant whose + declaring class is absent (0002 §5.3). So S3.8b introduces no constant type at all — + unlike `ClassLikeName` versus `ClassName`, which §7 leaves open. - **Steps 3 and 4 both edit `SymbolResolver` (§6).** S4.2 (positional extraction) is gated on S3.8 (the 3b lookup migration) so the two never run concurrently; manifest order keeps Step 3 ahead of Step 4 regardless. S4.1 (`TypeClassifier` + the §4.5/§4.6 From fad13d216e958a48968599131569c25161f1b7f2 Mon Sep 17 00:00:00 2001 From: Eric Stern Date: Mon, 17 Aug 2026 13:25:58 -0700 Subject: [PATCH 2/3] Name the constant FQN type; leave the resolved shape to SC.13 --- docs/architecture/0002-execution-plan.md | 32 ++++++++++++++---------- docs/architecture/build-manifest.md | 11 ++++---- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/docs/architecture/0002-execution-plan.md b/docs/architecture/0002-execution-plan.md index 643d698..510dbe6 100644 --- a/docs/architecture/0002-execution-plan.md +++ b/docs/architecture/0002-execution-plan.md @@ -641,21 +641,27 @@ To stop the two typing models fighting before they are built: - `QualifiedName` is the base FQN value type — a namespace path plus a short name, **kind-neutral**. -- `ClassLikeName`, `FunctionName`, `ConstantName`, `NamespaceName` extend / wrap it - and **carry their kind intrinsically** (each exposes `kind(): NameKind`). These are +- `ClassLikeName`, `FunctionName`, `GlobalConstantName`, `NamespaceName` extend / wrap + it and **carry their kind intrinsically** (each exposes `kind(): NameKind`). These are the primary currency; the per-kind `lookup*` methods take the matching one, so the kind is implicit and `NameKind` is not passed. `ClassLikeName` is today's `ClassName` (which per CLAUDE.md also serves as the class `Type`); whether it is reused as-is, renamed, or wrapped is an open decision (§7). The other three are new. -- **One constant type, class-declared or not.** `Domain\ConstantInfo` serves both: - `declaringClass` is `?ClassName`, and its absence is what makes a constant global. A - global constant is mechanically public and final, so `visibility` and `isFinal` carry - the true value rather than a placeholder, and `format()` branches on the declaring - class alone. `Domain\ConstantName` likewise holds either a member name or an FQN, so - the `ConstantName` above needs no second type. A global constant is **not** a - `ResolvedMember` — that interface means *reached through a class*, which is a path it - does not have — so `ConstantInfo` implements `ResolvedSymbol` itself rather than - gaining a fifth wrapper, which is also a down payment on the collapse in #416. +- **`GlobalConstantName`, because `ConstantName` is the class constant member name** + beside `MethodName` and `PropertyName`. A bare member name is not an FQN, and one type + holding both would report `NameKind::Constant` for a member name. Unlike + `ClassLikeName` versus `ClassName` in §7, these are two concepts, not two names. +- **One `ConstantInfo` serves both**, with `?ClassName` for `declaringClass`; its absence + is what makes a constant global, and `format()` branches on that alone. `visibility` + and `isFinal` carry true values — a redeclared `const` is fatal, a repeated `define()` + a no-op. The nullable is a conscious exception to the no-nullable rule: the + alternatives are a second metadata type differing in one field, or a sentinel + `ClassName` the type system cannot catch as a lie. +- A global constant is **not** a `ResolvedMember` — that interface means *reached through + a class*, which is a path it does not have. Whether it instead gets a fifth wrapper or + `ConstantInfo` carries `ResolvedSymbol` itself is #416's, and turns on SC.13: + `ResolvedSymbol` is in `Resolution` and returns an `Index\Location`, so a `Domain` + object carrying it adds edges the layer contract denies. - `locate(QualifiedName, NameKind)` is the kind-agnostic entry, used when the caller has an FQN whose kind is known only from syntactic position and has not minted a typed subtype. `NameKind` is **not** redundant here precisely because the input is @@ -665,7 +671,7 @@ To stop the two typing models fighting before they are built: is about identifiers, not search fragments. `kind` (once the parameter exists in Step 3b) selects which namespace to search. - **JIT:** Step 2 uses only `ClassLikeName` (today's `ClassName`) and `NamespaceName`. - `QualifiedName`, `NameKind`, `FunctionName`, and `ConstantName` land with the methods + `QualifiedName`, `NameKind`, `FunctionName`, and `GlobalConstantName` land with the methods that first use them (Step 3b, and `locate` in the workspace scope) — an unused type is not carried ahead of its method. This whole model is the *target*; it is introduced piecewise. @@ -823,7 +829,7 @@ once Step P is green. **Resolved — see Section 8.** - Whether `ClassLikeName` is the existing `ClassName` reused as-is, renamed, or a wrapper — it must coexist with `ClassName`'s dual role as the class `Type` (§5.3). -- Whether `FunctionName` / `ConstantName` / `NamespaceName` land in Step 2 as prep +- Whether `FunctionName` / `GlobalConstantName` / `NamespaceName` land in Step 2 as prep or in Step 3 with their lookups (lean: Step 3, to avoid an unused-type commit; `NamespaceName` is needed by `childrenOf` in Step 2, so it lands then). - Whether completion detail after the `search` migration comes from a follow-up diff --git a/docs/architecture/build-manifest.md b/docs/architecture/build-manifest.md index 939b3e9..ffc4729 100644 --- a/docs/architecture/build-manifest.md +++ b/docs/architecture/build-manifest.md @@ -182,12 +182,11 @@ Notes: of it. `NameKind` already exists (it predates Wave 2, as the catalog's coarse kind); Step 2 carries `ClassLikeName` / `NamespaceName`; `QualifiedName` lands in **S3.7b**, whose `DeclarationScanner` is its first caller; `FunctionName` in **S3.8a** and - `ConstantName` in **S3.8b**, with their lookups. - - **`ConstantName` is already taken, and stays one type.** `Domain\ConstantName` wraps - a *class* constant name; §5.3's `ConstantName` is a *global* constant FQN. One type - holds both, as does one `ConstantInfo`: a global constant is a constant whose - declaring class is absent (0002 §5.3). So S3.8b introduces no constant type at all — - unlike `ClassLikeName` versus `ClassName`, which §7 leaves open. + `GlobalConstantName` in **S3.8b**, with their lookups. + - **The `ConstantName` collision is settled (0002 §5.3):** the global FQN type is + `GlobalConstantName`, `Domain\ConstantName` stays the class member name, and one + `ConstantInfo` serves both kinds. The *resolved* shape stays open — it is #416's and + turns on SC.13, so take SC.13 first. - **Steps 3 and 4 both edit `SymbolResolver` (§6).** S4.2 (positional extraction) is gated on S3.8 (the 3b lookup migration) so the two never run concurrently; manifest order keeps Step 3 ahead of Step 4 regardless. S4.1 (`TypeClassifier` + the §4.5/§4.6 From e33481e55278ded4f5a916329fa667ee16c1c1a7 Mon Sep 17 00:00:00 2001 From: Eric Stern Date: Mon, 17 Aug 2026 13:54:37 -0700 Subject: [PATCH 3/3] Carry the rename into the SymbolSource sketch --- docs/architecture/0002-execution-plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/0002-execution-plan.md b/docs/architecture/0002-execution-plan.md index 510dbe6..f18ffe8 100644 --- a/docs/architecture/0002-execution-plan.md +++ b/docs/architecture/0002-execution-plan.md @@ -602,7 +602,7 @@ interface SymbolSource // --- Added JIT, when the step that needs them lands (NOT built in Step 2) --- // Step 3b (functions/constants gain project reach; a second searchable kind exists): // lookupFunction(FunctionName): ?FunctionInfo - // lookupConstant(ConstantName): ?ConstantInfo + // lookupConstant(GlobalConstantName): ?ConstantInfo // searchClassLikes generalizes to search(string $prefix, NameKind $kind) // Future (workspace scope, #264): // locate(QualifiedName, NameKind): ?SymbolDefinition // kind-neutral def-site, only if a feature needs it