# 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.