diff --git a/docs/compiler/pbs/agendas/19. Globals, Synthetic Module Init, and FRAME_RET Agenda.md b/docs/compiler/pbs/agendas/19. Globals, Synthetic Module Init, and FRAME_RET Agenda.md new file mode 100644 index 00000000..26264557 --- /dev/null +++ b/docs/compiler/pbs/agendas/19. Globals, Synthetic Module Init, and FRAME_RET Agenda.md @@ -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. diff --git a/docs/compiler/pbs/agendas/README.md b/docs/compiler/pbs/agendas/README.md index 8a9ad6cb..5f90ce08 100644 --- a/docs/compiler/pbs/agendas/README.md +++ b/docs/compiler/pbs/agendas/README.md @@ -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