globals and synthetic inits
This commit is contained in:
parent
b00b4e3eac
commit
f49dc64fe0
@ -0,0 +1,286 @@
|
||||
# Globals, Synthetic Module Init, and FRAME_RET Agenda
|
||||
|
||||
## Status
|
||||
|
||||
Open
|
||||
|
||||
## Domain Owner
|
||||
|
||||
`docs/compiler/pbs`
|
||||
|
||||
Este tema pertence ao domínio PBS do compiler porque afeta diretamente:
|
||||
|
||||
- a surface language de declarações top-level;
|
||||
- a detecção de entrypoints pelo frontend PBS;
|
||||
- o modelo de inicialização de módulo e programa;
|
||||
- o lowering de entrypoint de frame;
|
||||
- a relação entre entrypoint lógico do usuário e entrypoint publicado no artefato;
|
||||
- o contrato interno entre frontend, `IRBackend`, `IRVM` lowering e bytecode final.
|
||||
|
||||
Nesta fase, o owner continua sendo `compiler/pbs` mesmo quando o resultado reutilizar capacidades já existentes da VM, porque a discussão é sobre como o compiler vai expor e orquestrar essas capacidades.
|
||||
|
||||
## Problema
|
||||
|
||||
Hoje o PBS não expõe variáveis globais mutáveis de módulo.
|
||||
|
||||
O topo do arquivo aceita `declare const`, mas `declare const` é compile-time e não storage runtime. Isso impede formas como:
|
||||
|
||||
```pbs
|
||||
declare struct Vec2(x: int, y: int);
|
||||
|
||||
declare global origin: Vec2 = new Vec2(0, 0);
|
||||
```
|
||||
|
||||
Ao mesmo tempo:
|
||||
|
||||
1. a VM já tem suporte operacional para globals por slot;
|
||||
2. o compiler não modela globals no IR executável atual;
|
||||
3. o entrypoint de frame do usuário hoje coincide, na prática, com a função que delimita o frame lógico;
|
||||
4. o pipeline atual usa `FRAME_RET` no final desse fluxo de frame lógico.
|
||||
5. a seleção do entrypoint ainda depende de configuração explícita em `FrontendSpec`, em vez de ser derivada da própria source language do PBS.
|
||||
|
||||
Se introduzirmos `declare global`, o compiler precisará definir:
|
||||
|
||||
1. como globals são inicializados;
|
||||
2. quando essa inicialização roda;
|
||||
3. como garantir execução uma única vez;
|
||||
4. como conviver com um `init()` custom do usuário;
|
||||
5. como detectar de forma canônica quais funções do usuário exercem os papéis de `init` e `frame`;
|
||||
6. como preservar o contrato de frame lógico quando a função publicada deixar de ser a função `frame()` original do usuário.
|
||||
|
||||
## Contexto
|
||||
|
||||
O estado atual do sistema é:
|
||||
|
||||
- PBS proíbe `let` top-level e não expõe storage mutável de módulo;
|
||||
- `declare const` é avaliado em compile time e pode ser inlinado/foldado;
|
||||
- inicializadores não constantes como `new`, call sugar, `some`, `none`, `if`, `switch` e outras formas executáveis não pertencem ao modelo de `declare const`;
|
||||
- a VM do runtime já possui `GetGlobal` e `SetGlobal`;
|
||||
- o `IRBackend` executável atual modela locals, calls, jumps e literais, mas não modela globals;
|
||||
- o entrypoint de frame hoje é tratado como raiz do frame lógico, inclusive para o ponto final onde `FRAME_RET` é emitido;
|
||||
- o frontend PBS ainda depende de configuração explícita em `FrontendSpec` para saber qual callable é o entrypoint publicado;
|
||||
- hoje isso aparece como acoplamento a nomes/configuração que deveriam ser deduzidos pela própria linguagem.
|
||||
|
||||
O cenário motivador é permitir globals de módulo no PBS sem exigir, nesta fase, mudança de runtime.
|
||||
|
||||
Também queremos aproveitar essa discussão para mover a detecção de entrypoint para a source language do PBS, usando atributos explícitos:
|
||||
|
||||
```pbs
|
||||
[INIT]
|
||||
fn init() -> void {}
|
||||
|
||||
[FRAME]
|
||||
fn frame() -> void {}
|
||||
```
|
||||
|
||||
Essa direção permitiria ao frontend PBS:
|
||||
|
||||
1. localizar explicitamente o init do usuário;
|
||||
2. localizar explicitamente o frame root do usuário;
|
||||
3. montar o wrapper sintético final sem depender de configuração paralela em `FrontendSpec`;
|
||||
4. eliminar, nesta linha de evolução, a configuração explícita atual de entrypoint no registry/spec do frontend.
|
||||
|
||||
Esta agenda também cobre a extensão desse modelo para discutir:
|
||||
|
||||
1. se `[INIT]` deve existir apenas como hook final de programa ou também como surface por módulo;
|
||||
2. qual é exatamente o comportamento operacional de `[FRAME]` como root lógico de frame do usuário;
|
||||
3. como `[INIT]` por módulo, `[INIT]` final de programa, wrapper sintético e `FRAME_RET` se compõem sem ambiguidade.
|
||||
|
||||
Isso empurra a solução para o compiler:
|
||||
|
||||
1. synth de init por módulo;
|
||||
2. synth de wrapper do frame published entrypoint;
|
||||
3. eventual símbolo privado `boot_done`;
|
||||
4. redefinição do ponto onde o frame lógico realmente termina;
|
||||
5. detecção de `init`/`frame` a partir de atributos de source;
|
||||
6. remoção progressiva das configs explícitas atuais em `FrontendSpec`.
|
||||
|
||||
## Core Questions
|
||||
|
||||
1. PBS deve introduzir `declare global` como declaração top-level distinta de `declare const`?
|
||||
2. `declare global` deve aceitar inicializadores executáveis como `new Vec2(...)`, calls e outras expressões lowerables?
|
||||
3. O compiler deve sintetizar um module init por módulo que contenha `declare global`?
|
||||
4. A inicialização deve rodar eager no boot lógico do programa ou lazy no primeiro frame?
|
||||
5. Deve existir um `init()` custom do usuário que roda depois de todos os module inits e antes do primeiro `frame()`?
|
||||
6. O PBS deve introduzir atributos explícitos `[INIT]` e `[FRAME]` para identificar as funções corretas do usuário?
|
||||
7. A descoberta de entrypoint no frontend PBS deve deixar de depender de configuração explícita em `FrontendSpec`?
|
||||
8. `[INIT]` deve poder existir também em escopo/módulo de forma declarativa para orientar module init explícito, ou o compiler deve manter module init sempre totalmente sintético?
|
||||
9. Qual é o contrato preciso de comportamento de `[FRAME]` no PBS: callable obrigatório, assinatura fixa, frequência esperada e relação com o frame lógico publicado?
|
||||
10. O published frame entrypoint deve passar a ser um wrapper sintético, por exemplo `__pbs_frame()`, em vez do `frame()` do usuário?
|
||||
11. `FRAME_RET` deve continuar significando "fim do frame lógico" e apenas mudar de owner, saindo da função do usuário para o wrapper sintético?
|
||||
12. Como o compiler deve tratar ordem topológica, dependências e ciclos entre globals de módulos distintos?
|
||||
13. Qual política vale quando algum module init ou `init()` do usuário falha: retry no próximo frame, fail-fast, ou outra forma?
|
||||
14. Globals exportados por `mod.barrel` devem se comportar como storage compartilhado de módulo ou como snapshot/importação por valor?
|
||||
|
||||
## Inputs Already Fixed Elsewhere
|
||||
|
||||
Os seguintes pontos já parecem fixos ou fortemente estabelecidos e não devem ser contraditos nesta agenda:
|
||||
|
||||
- a VM já suporta globals em nível de bytecode/execução;
|
||||
- `declare const` não é storage mutável/runtime-initialized;
|
||||
- o runtime já sabe executar um entrypoint published por frame;
|
||||
- `FRAME_RET` já é usado como marcador do fim de um frame lógico;
|
||||
- o frontend PBS hoje ainda carrega configuração explícita de entrypoint fora da source language;
|
||||
- nesta fase queremos evitar mudanças novas no runtime e concentrar a evolução no compiler.
|
||||
|
||||
## Options
|
||||
|
||||
### Option A
|
||||
|
||||
Adicionar `declare global`, introduzir `[INIT]` e `[FRAME]` como superfícies explícitas para lifecycle/entrypoint, discutir `[INIT]` final vs `[INIT]` por módulo, gerar module init sintético por módulo, gerar wrapper sintético de frame published entrypoint e mover o `FRAME_RET` para esse wrapper.
|
||||
|
||||
### Option B
|
||||
|
||||
Adicionar `declare global`, mas exigir init totalmente estático ou restrito, sem `init()` custom do usuário e sem wrapper published separado do `frame()` atual.
|
||||
|
||||
### Option C
|
||||
|
||||
Não adicionar `declare global` agora; manter apenas `declare const` e postergar a discussão até existir um modelo maior de bootstrap/program lifecycle.
|
||||
|
||||
### Option D
|
||||
|
||||
Adicionar `declare global`, mas exigir lazy materialization no primeiro acesso em vez de module init explícito por ordem topológica.
|
||||
|
||||
## Tradeoffs
|
||||
|
||||
### Option A
|
||||
|
||||
- Prós:
|
||||
- modelo explícito e fácil de explicar;
|
||||
- acomoda `declare global` com inicializadores realmente executáveis;
|
||||
- faz o PBS declarar na própria linguagem quais são os callables de `init` e `frame`;
|
||||
- abre espaço para discutir de forma unificada `init` por módulo, `init` final e root de frame;
|
||||
- permite `init()` custom do usuário sem mudar o runtime;
|
||||
- preserva o significado de `FRAME_RET` como fim do frame lógico;
|
||||
- remove um acoplamento indesejado entre frontend PBS e configuração manual de entrypoint em `FrontendSpec`.
|
||||
- Contras:
|
||||
- exige nova modelagem no compiler para globals, init synthesis e published frame wrapper;
|
||||
- exige regras novas de atributo e validação de unicidade para `[INIT]` e `[FRAME]`;
|
||||
- obriga decisão clara sobre ordem, ciclos e falhas;
|
||||
- muda a relação atual entre `frame()` do usuário e entrypoint publicado.
|
||||
|
||||
### Option B
|
||||
|
||||
- Prós:
|
||||
- escopo menor;
|
||||
- reduz a superfície de lifecycle;
|
||||
- pode evitar parte da complexidade de wrapper e boot state.
|
||||
- Contras:
|
||||
- resolve mal o caso motivador com `new Vec2(...)`;
|
||||
- produz uma feature de globais com restrição semântica forte demais;
|
||||
- deixa o `init()` do usuário para outra rodada e pode forçar redesign posterior.
|
||||
|
||||
### Option C
|
||||
|
||||
- Prós:
|
||||
- nenhum risco imediato em pipeline/backend;
|
||||
- preserva o estado atual do compiler.
|
||||
- Contras:
|
||||
- não resolve o caso desejado;
|
||||
- mantém uma lacuna entre capacidade da VM e surface language do PBS;
|
||||
- posterga um problema arquitetural que já apareceu de forma concreta.
|
||||
|
||||
### Option D
|
||||
|
||||
- Prós:
|
||||
- evita bootstrap eager explícito;
|
||||
- pode reduzir custo de init em módulos não usados.
|
||||
- Contras:
|
||||
- complica determinismo e observabilidade;
|
||||
- torna mais difícil explicar dependências entre módulos;
|
||||
- aumenta risco semântico para reentrância, ciclos e ordem de efeitos;
|
||||
- parece desnecessariamente sofisticado para v1.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Seguir com a **Option A**.
|
||||
|
||||
Direção recomendada para discussão:
|
||||
|
||||
1. introduzir `declare global` como nova declaração top-level distinta de `declare const`;
|
||||
2. permitir inicializadores executáveis lowerables, incluindo `new` e calls compatíveis com o modelo de lowering executável;
|
||||
3. sintetizar um module init por módulo owner de globals;
|
||||
4. ordenar os module inits de forma determinística e topológica;
|
||||
5. introduzir `[INIT]` e `[FRAME]` como superfícies explícitas de detecção do init/frame do usuário;
|
||||
6. remover da evolução do PBS a dependência de configuração explícita de entrypoint em `FrontendSpec`, migrando a seleção para a análise da source language;
|
||||
7. discutir explicitamente se `[INIT]` por módulo será uma surface do usuário ou se módulo continuará com init apenas sintético;
|
||||
8. permitir um `init()` custom opcional do usuário, executado depois de todos os module inits;
|
||||
9. publicar um wrapper sintético de frame, por exemplo `__pbs_frame()`, como verdadeiro frame root;
|
||||
10. tratar `frame()` do usuário como callable normal invocado por esse wrapper;
|
||||
11. manter `FRAME_RET` como marcador do fim do frame lógico, mas emitido no wrapper sintético publicado em vez de no `frame()` do usuário;
|
||||
12. manter toda a primeira fase restrita ao compiler e ao backend pipeline, sem novos requisitos para o runtime além dos contratos já existentes.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. O nome surface deve ser exatamente `declare global`, ou outra forma top-level?
|
||||
2. Globals devem exigir initializer obrigatório em v1, ou existirão shells reservados parecidos com builtin const?
|
||||
3. `[INIT]` e `[FRAME]` devem ser atributos reservados do PBS ou outra forma de marker surface?
|
||||
4. `[INIT]` deve exigir assinatura fixa `fn init() -> void`?
|
||||
5. `[FRAME]` deve exigir assinatura fixa `fn frame() -> void`?
|
||||
6. Deve existir no máximo um `[INIT]` e um `[FRAME]` por programa, por módulo owner, ou por outro escopo?
|
||||
7. `[INIT]` por módulo deve ser permitido como surface explícita do usuário ou ser non-goal da primeira fase?
|
||||
8. Se `[INIT]` por módulo existir, ele roda antes ou no lugar do module init sintético de globals?
|
||||
9. A ausência de `[INIT]` deve ser válida, mas a ausência de `[FRAME]` deve ser erro hard?
|
||||
10. O `init()` do usuário pertence apenas ao módulo owner do published frame entrypoint ou pode vir de qualquer módulo visível?
|
||||
11. O compiler deve sintetizar `boot_done` como global oculto de programa, local estático do wrapper, ou outro mecanismo interno?
|
||||
12. Em caso de trap/falha durante module init ou `init()`, o próximo frame deve tentar novamente ou o programa deve entrar em estado terminal?
|
||||
13. Módulos podem ler globals de outros módulos durante init, desde que a ordem topológica já os tenha materializado?
|
||||
14. Como diagnosticar ciclos entre globals intra-módulo e inter-módulo de forma didática?
|
||||
15. Qual é a semântica exata de `[FRAME]` para o compiler e para a documentação da linguagem: raiz de tick, raiz de frame lógico, ou apenas callable nomeado para publicação?
|
||||
16. O barrel/export de `global` deve seguir a mesma superfície de `const`, ou precisamos de distinção explícita na documentação/export metadata?
|
||||
17. O `IRBackend` deve ganhar instruções explícitas `GET_GLOBAL`/`SET_GLOBAL`, ou globals devem ser lowered por outra representação intermediária antes de virar bytecode?
|
||||
18. Em que etapa da migração a configuração explícita atual em `FrontendSpec` deixa de existir por completo?
|
||||
|
||||
## Expected Decisions to Produce
|
||||
|
||||
1. A surface language e a gramática de `declare global`.
|
||||
2. A surface language e a política de detecção de entrypoint via `[INIT]` e `[FRAME]`.
|
||||
3. O papel de `[INIT]` por módulo versus module init totalmente sintético.
|
||||
4. O contrato semântico de globals de módulo em PBS.
|
||||
5. O modelo de inicialização sintética por módulo.
|
||||
6. O contrato de `init()` custom do usuário.
|
||||
7. A semântica comportamental de `[FRAME]` como root lógico do usuário.
|
||||
8. A política de ordering e ciclo para init/global dependencies.
|
||||
9. O novo owner do `FRAME_RET` no pipeline de frame.
|
||||
10. O plano de remoção das configs explícitas atuais em `FrontendSpec`.
|
||||
11. O recorte exato desta fase como mudança somente de compiler.
|
||||
|
||||
## Expected Spec Material
|
||||
|
||||
- atualização da Core Syntax specification para `declare global`;
|
||||
- atualização da Core Syntax specification para `[INIT]` e `[FRAME]`;
|
||||
- atualização da Static Semantics specification para typing, visibility e rules de init/global;
|
||||
- atualização da Dynamic Semantics specification para lifecycle de globals e boot one-shot;
|
||||
- atualização da Lowering spec para globals, module init synthesis, entrypoint detection e published frame wrapper;
|
||||
- eventual atualização de AST/IR contracts se essas superfícies forem documentadas.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- redefinir a VM para adicionar uma nova capacidade de globals;
|
||||
- introduzir um novo hook explícito de runtime nesta fase;
|
||||
- redesenhar todo o lifecycle de cartridge/firmware fora do que o compiler já publica como entrypoint;
|
||||
- resolver lazy module loading ou hot reload;
|
||||
- manter indefinidamente configuração duplicada de entrypoint em `FrontendSpec` e na source language;
|
||||
- projetar persistência/snapshot/save-state de globals como parte desta discussão inicial.
|
||||
|
||||
## Next Step Suggested
|
||||
|
||||
Converter esta agenda em uma `decision` do domínio `compiler/pbs` fechando:
|
||||
|
||||
1. a forma de `declare global`;
|
||||
2. a superfície `[INIT]`/`[FRAME]` e a nova detecção de entrypoints;
|
||||
3. o modelo de module init sintético;
|
||||
4. o papel do `init()` do usuário;
|
||||
5. o published frame wrapper;
|
||||
6. o reposicionamento do `FRAME_RET`;
|
||||
7. a remoção da configuração explícita atual em `FrontendSpec`.
|
||||
|
||||
Depois disso, abrir um `pull-request/plan` que separe explicitamente:
|
||||
|
||||
1. parser/AST;
|
||||
2. semântica de atributos `[INIT]`/`[FRAME]` e resolução de entrypoint;
|
||||
3. semântica e barrel/export/import;
|
||||
4. IR/backend model para globals;
|
||||
5. synthesis de init e frame wrapper;
|
||||
6. remoção das configs explícitas antigas no frontend registry/specs;
|
||||
7. testes de regressão do pipeline compilador -> IR -> bytecode.
|
||||
@ -12,6 +12,7 @@ Closed agendas are moved to `docs/pbs/agendas/archive`.
|
||||
4. `18.3. Backend Workshop 3 - Bytecode Marshaling and Runtime Conformance.md`
|
||||
5. `18.4. Asset References in Game Code - Names vs Compile-Time Lowering Agenda.md`
|
||||
6. `18.5. Ignored Call Results in Executable Lowering Agenda.md`
|
||||
7. `19. Globals, Synthetic Module Init, and FRAME_RET Agenda.md`
|
||||
|
||||
## Purpose
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user