prometeu-studio/docs/compiler/pbs/agendas/19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda.md

407 lines
17 KiB
Markdown

# PBS Globals Surface, Identity, and Module Boundaries Agenda
## Status
Open
## Parent Agenda
This agenda derives from:
- `19. Globals, Synthetic Module Init, and FRAME_RET Agenda`
This is the first derived discussion under umbrella topic `19`.
## Purpose
Define the source-level and module-level model for PBS runtime globals before lifecycle, entrypoint publication, or lowering details are discussed.
This agenda exists to close the semantic base for:
- the `declare global` surface;
- storage ownership;
- import/export/alias behavior;
- and dependency/cycle rules at module boundary level.
Later agendas under topic `19` must treat the outputs of this agenda as fixed input.
## Domain Owner
`docs/compiler/pbs`
Este tema pertence ao domínio PBS do compiler porque define surface language, linking semantics e boundaries de módulo para uma nova categoria de declaração top-level.
## Context
Hoje o PBS já possui `declare const`, mas esse surface é estritamente compile-time:
- `declare const` entra no value namespace;
- `declare const` continua reservado a valores imutáveis que não representam storage mutável de módulo;
- `declare const` não materializa storage mutável runtime;
- o initializer de `declare const` pertence a um subset constante;
- dependências de `declare const` são resolvidas por análise de dependência compile-time.
Ao mesmo tempo:
- a VM já expõe `GET_GLOBAL` e `SET_GLOBAL`;
- a linguagem ainda não expõe globals mutáveis de módulo;
- `mod.barrel` hoje já governa export/import de `const`, `fn`, tipos e outras famílias de declaração;
- não existe ainda contrato explícito para storage compartilhado intermodular em source PBS.
Antes de discutir init, frame wrapper ou `FRAME_RET`, precisamos fechar o que um `global` é no modelo da linguagem.
## Inputs Already Fixed Elsewhere
Os seguintes inputs já parecem fixos e não devem ser reabertos aqui:
- `declare const` não é storage mutável/runtime-initialized;
- `declare const` continua semanticamente separado de `declare global`;
- `declare const` continua voltado a valores imutáveis e não deve ser reutilizado como surface de global;
- modules e globals devem compartilhar o mesmo tipo estrutural de análise de dependência;
- essa análise comum deve vir de um refactor do algoritmo atual de modules para um `DependencyGraphAnaliser` em `util.structures` na infra;
- top-level executable statements continuam proibidos;
- a VM já suporta globals por slot;
- esta fase ainda é compiler-driven e não deve exigir capability nova do runtime;
- barrel continua sendo a fonte de visibilidade entre módulos.
## Decisions To Produce
Esta agenda deve produzir direção suficiente para fechar:
1. a surface exata de `declare global`;
2. a obrigatoriedade de initializer em todos os globals de v1;
3. o namespace ocupado por globals;
4. a política de visibility/barrel para globals;
5. a identidade do storage owner;
6. a semântica de import, export e alias;
7. a política de dependência e ciclos entre globals.
## Core Questions
1. PBS deve introduzir exatamente `declare global name: T = expr;` como nova forma top-level?
2. `declare global` deve exigir type annotation explícita em todos os casos?
3. Quais formas de initializer executável são admissíveis para `declare global` em v1?
4. `declare global` entra no mesmo value namespace de `let` e `declare const`?
5. O barrel deve ganhar uma entrada `global` distinta?
6. Um `global` importado referencia sempre o storage do módulo owner original?
7. Como imports de globals usam alias quando necessário sem introduzir shadowing?
8. O que é proibido em imports de globals para evitar ambiguidade?
9. A dependência entre globals deve ser modelada por grafo determinístico semelhante ao de `declare const`, mas agora com efeito runtime?
10. Como ciclos entre globals intra-módulo e inter-módulo devem ser detectados e reportados?
11. Como o grafo de globals mapeia para o `DependencyGraphAnaliser` comum sem perder owner canônico nem attribution?
## Options
### Option A
Adicionar `declare global` como nova declaração top-level explícita, com type annotation obrigatória, initializer obrigatório em v1, entry própria `global` em barrel, sem reexport e identidade de storage sempre pertencente ao módulo owner original.
### Option B
Adicionar `declare global`, mas permitir globals sem initializer em v1 e tratar inicialização default como parte do modelo base.
## Tradeoffs
### Option A
- Prós:
- semântica explícita e fácil de ensinar;
- evita ambiguidade entre constant value e mutable storage;
- força o barrel e os imports a refletirem corretamente a nova categoria declarativa;
- reduz risco de globals parcialmente formados sem política clara de init.
- Contras:
- aumenta a superfície da linguagem;
- exige atualização de parser, AST, linking e barrel matching.
### Option B
- Prós:
- maior flexibilidade futura;
- permite reservar globals antes de decidir a inicialização concreta.
- Contras:
- empurra complexidade para lifecycle e diagnostics;
- enfraquece a previsibilidade de v1;
- cria edge cases sobre leitura antes de materialização.
## Recommendation
Seguir com a **Option A**.
Direção recomendada:
1. `declare global` deve ser uma declaração top-level distinta de `declare const`;
2. type annotation explícita deve ser obrigatória;
3. initializer deve ser obrigatório em v1;
4. `declare const` não deve ser reutilizado para globals e continua reservado a valores imutáveis;
5. globals devem entrar no value namespace, mas com categoria declarativa distinta;
6. barrel deve refletir essa distinção explicitamente com `mod global` e `pub global`;
7. PBS não deve introduzir reexport de globals nesta linha;
8. imports de globals devem preservar a identidade do storage owner original e usar alias quando necessário;
9. shadowing entre imports de `fn`, `service`, `global` e `const` deve ser compile-time error;
10. dependências entre globals devem ser determinísticas e diagnosticáveis.
Também fica registrado nesta agenda um direction técnico já decidido:
1. modules e globals usarão o mesmo tipo estrutural de análise de dependência;
2. isso deve nascer de um refactor do algoritmo atual de modules;
3. o artefato comum deve viver na infra como `DependencyGraphAnaliser` em `util.structures`;
4. cada domínio continua responsável por construir seus próprios nós canônicos, suas arestas e seu mapeamento de diagnóstico;
5. o que é compartilhado é o kernel estrutural de ordenação topológica e detecção de ciclos, não a semântica específica de cada grafo.
## Global Dependencies
O ponto mais sensível desta agenda é a dependência entre globals.
Com `declare const`, dependência significa apenas ordem de avaliação compile-time.
Com `declare global`, dependência passa a significar ordem de materialização runtime, identidade de storage e risco real de efeito parcial.
Exemplos do tipo de dependência que passam a existir:
```pbs
declare global a: int = 1;
declare global b: int = a + 1;
```
Aqui `b` depende de `a` porque o initializer de `b` lê o valor de `a`.
```pbs
declare global origin: Vec2 = new Vec2(0, 0);
declare global camera: Camera = new Camera(origin);
```
Aqui `camera` depende de `origin` porque o initializer usa o valor já materializado de outro global.
Quando há múltiplos módulos, o problema cresce:
```pbs
// module A
declare global seed: int = 7;
// module B
import { seed } from @project:A;
declare global rng: Rng = new Rng(seed);
```
Agora a ordem de materialização não é apenas intra-módulo. Ela atravessa módulos e precisa continuar determinística.
### Dependency Kinds
Esta agenda precisa distinguir pelo menos três formas de dependência:
1. dependência local entre globals do mesmo módulo;
2. dependência intermodular por import de global do módulo owner original;
3. dependência indireta por alias de import, onde o nome local muda, mas o owner do storage permanece igual.
### Main Problems
Os principais problemas de dependência de globals são:
1. ordem de inicialização deixa de poder depender de ordem textual do arquivo;
2. imports com alias podem esconder o owner real do storage;
3. leitura de global ainda não materializado precisa ser proibida por modelo, não tratada como comportamento oportunista;
4. ciclos deixam de ser apenas "ciclos de expressão" e passam a ser ciclos de materialização runtime;
5. o grafo de dependências pode cruzar múltiplos módulos e aliases, o que piora atribuição de diagnóstico.
### Risks
Se a política de dependência de globals ficar frouxa, os riscos são altos:
1. inicialização parcialmente observável:
um global pode ler outro antes de ele existir semanticamente;
2. ordem acidental por arquivo:
a implementação pode acabar usando ordem textual ou ordem de carregamento do projeto, o que quebra determinismo;
3. aliasing ambíguo:
aliases de import podem parecer símbolo novo quando na verdade são só outro nome para o mesmo owner;
4. ciclos opacos:
`A -> B -> C -> A` pode ficar escondido atrás de import e alias, dificultando explicação ao usuário;
5. bootstrap contaminado:
se essa agenda deixar dependência frouxa, a 19.2 herda um lifecycle impossível de explicar;
6. lowering prematuro:
se o compiler materializar a ordem no backend sem política semântica firme, a arquitetura congela cedo demais.
### Direction for Discussion
Minha direção recomendada para dependências de globals nesta agenda é:
1. toda leitura de outro global no initializer cria uma edge explícita no grafo de dependência;
2. a resolução do grafo deve ser determinística e independente de source-file order;
3. imports e aliases devem preservar o owner original do storage no grafo;
4. ciclos entre globals devem ser erro estático, não comportamento runtime com retry ou valor parcial;
5. a agenda deve deixar claro que esta política fecha apenas identidade e dependência;
boot ordering completo continua pertencendo à `19.2`.
### Structural Reuse Decision
Esta agenda também registra uma decisão arquitetural de implementação que afeta a evolução futura:
1. o algoritmo atual usado para dependência de modules deve ser refatorado em um componente estrutural reutilizável;
2. esse componente comum deve se chamar `DependencyGraphAnaliser`;
3. ele deve viver em `util.structures` na infra;
4. modules e globals devem consumir esse mesmo componente estrutural;
5. a semântica que descobre edges continua separada por domínio.
Isso significa:
- o import graph de modules e o init graph de globals não serão o mesmo grafo semântico;
- mas ambos deverão usar o mesmo kernel estrutural para:
- topological ordering,
- cycle detection,
- e suporte a diagnóstico determinístico.
## Current Direction
Os pontos abaixo já podem ser tratados como direção fechada desta agenda, salvo objeção nova forte:
1. a surface correta é `declare global BLA: int = 1;`;
2. type annotation é obrigatória, como em `declare const`;
3. initializer é sempre obrigatório;
4. globals entram no value namespace como categoria declarativa distinta;
5. `mod.barrel` deve expor globals com entradas `mod global Name;` e `pub global Name;`;
6. imports de globals usam o mesmo esquema de alias quando necessário;
7. PBS não introduz reexport de globals nesta linha;
8. shadowing entre imports de `fn`, `service`, `global` e `const` é compile-time error e deve ser resolvido com alias;
9. detecção de dependência, ordenação e ciclo deve nascer do `DependencyGraphAnaliser` comum, com contexto semântico próprio de globals.
Também fica explicitado o modelo de namespace:
1. globals entram no value namespace;
2. `declare const`, `declare global` e o singleton canônico de `service` convivem no espaço nominal de valores;
3. `fn`, `service`, `global` e `const` não podem introduzir o mesmo nome visível no mesmo scope de resolução;
4. essa colisão é erro de compile time, mesmo quando a ambiguidade nasce de import;
5. `locals > struct/class members > globals`, inclusive quando o global veio por import.
## Remaining Open Points
Com a direção já consolidada nesta agenda, não restam pontos substantivos em aberto dentro do boundary de `19.1`.
Os temas adicionais de lifecycle seguem encaminhados para a `19.2`.
## Initializer Boundary
Esta agenda também precisa deixar explícita a fronteira entre:
1. init da declaração `global`;
2. init por módulo;
3. init de projeto.
Direção recomendada:
- o initializer de `declare global` deve permanecer restrito e simples;
- ele existe para materializar o valor inicial da storage de módulo;
- ele não deve carregar lógica procedural maior nem depender de top-level `fn`;
- lógica adicional de setup pertence ao init por módulo;
- setup de programa pertence ao init de projeto.
Com isso, a linha proposta fica:
1. init da global:
permite primitivas, operações simples com valor, member access com valor nessa etapa, `new`, e leitura de outras globals compatíveis com o dep graph;
2. init do módulo:
permite lógica adicional de conveniência e mutações nas globals do próprio módulo após a materialização inicial;
3. init do projeto:
permite setup do projeto como um todo, depois do fechamento do estado de módulo necessário.
### Consequence for `fn` in Global Initializers
Seguindo essa separação, esta agenda recomenda que:
1. calls para top-level `fn` não sejam permitidas em initializer de `global` em v1;
2. a necessidade de lógica mais rica seja absorvida pelo init de módulo discutido na `19.2`;
3. isso mantenha o dep graph de globals focado em materialização declarativa, não em execução procedural arbitrária.
Também fica recomendado que, em v1:
1. `some(...)` e `none` não façam parte do initializer declarativo de `global`;
2. `if` e `switch` não sejam permitidos diretamente na declaração de `global`.
## Diagnostics Recommendation
Esta agenda fecha o seguinte catálogo pequeno, forte e didático de diagnósticos obrigatórios.
### 1. Invalid Global Initializer
Quando o initializer usar uma forma não admissível em v1, o compiler deve emitir um erro apontando a subexpressão inválida.
Mensagem semântica recomendada:
- `global initializer uses unsupported form`
Exemplos de motivo associado:
- top-level `fn` call not allowed in global initializer;
- `some(...)` not allowed in global initializer;
- `if` not allowed in global initializer.
### 2. Global Dependency Cycle
Quando houver ciclo, o compiler deve diagnosticar o global owner local e, quando possível, renderizar o caminho canônico do ciclo.
Mensagem semântica recomendada:
- `global dependency cycle detected`
Detalhe recomendado:
- incluir o símbolo local que participa do ciclo;
- incluir o caminho canônico quando disponível, por exemplo:
`@project:A.seed -> @project:B.rng -> @project:C.config -> @project:A.seed`
### 3. Shadowing Requires Alias
Quando um import introduzir nome que colide com `fn`, `service`, `global` ou `const` já visível, o compiler deve rejeitar e exigir alias explícito.
Mensagem semântica recomendada:
- `imported symbol shadows existing visible symbol; alias required`
### 4. Global Import Category Mismatch
Se o barrel e a importação não preservarem corretamente a categoria declarativa `global`, o compiler deve rejeitar explicitamente em vez de tentar degradar para `const` ou valor genérico.
Mensagem semântica recomendada:
- `global import must resolve through a global barrel entry`
Essa escolha existe para preservar:
- clareza semântica;
- diagnósticos mais simples;
- e uma separação limpa entre storage initialization e lifecycle hooks.
## Forwarded To 19.2
Os seguintes temas surgiram desta agenda, mas pertencem ao boundary de lifecycle e devem ser tratados na `19.2`:
1. init por módulo como fase procedural separada do initializer declarativo de `global`;
2. init de projeto como fase distinta de coordenação final;
3. permissão de loops em init de módulo e init de projeto;
4. proibição de host calls por padrão durante init;
5. permissão apenas para host calls marcadas com atributo reservado `[InitAllowed]`;
6. validação compile-time dessa admissibilidade;
7. separação entre restrição normativa de host interaction e recomendação de design para manter bootstrap leve.
## Expected Spec Material
Se esta agenda fechar, a propagação esperada atinge pelo menos:
- `3. Core Syntax Specification.md`
- `4. Static Semantics Specification.md`
- `11. AST Specification.md`
- `12. Diagnostics Specification.md`
## Non-Goals
Esta agenda não deve:
1. definir boot ordering;
2. definir `[Init]` ou `[Frame]`;
3. definir wrapper published entrypoint;
4. definir posicionamento de `FRAME_RET`;
5. escolher a representação de IR/bytecode para globals.
## Next Step
Depois de fechar esta agenda, abrir ou aprofundar:
- `19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda`