407 lines
17 KiB
Markdown
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`
|