15 KiB
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,IRVMlowering 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:
declare struct Vec2(x: int, y: int);
declare global origin: Vec2 = new Vec2(0, 0);
Ao mesmo tempo:
- a VM já tem suporte operacional para globals por slot;
- o compiler não modela globals no IR executável atual;
- o entrypoint de frame do usuário hoje coincide, na prática, com a função que delimita o frame lógico;
- o pipeline atual usa
FRAME_RETno final desse fluxo de frame lógico. - 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:
- como globals são inicializados;
- quando essa inicialização roda;
- como garantir execução uma única vez;
- como conviver com um
init()custom do usuário; - como detectar de forma canônica quais funções do usuário exercem os papéis de
initeframe; - 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
lettop-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,switche outras formas executáveis não pertencem ao modelo dedeclare const; - a VM do runtime já possui
GetGlobaleSetGlobal; - o
IRBackendexecutá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
FrontendSpecpara 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:
[INIT]
fn init() -> void {}
[FRAME]
fn frame() -> void {}
Essa direção permitiria ao frontend PBS:
- localizar explicitamente o init do usuário;
- localizar explicitamente o frame root do usuário;
- montar o wrapper sintético final sem depender de configuração paralela em
FrontendSpec; - 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:
- se
[INIT]deve existir apenas como hook final de programa ou também como surface por módulo; - qual é exatamente o comportamento operacional de
[FRAME]como root lógico de frame do usuário; - como
[INIT]por módulo,[INIT]final de programa, wrapper sintético eFRAME_RETse compõem sem ambiguidade.
Isso empurra a solução para o compiler:
- synth de init por módulo;
- synth de wrapper do frame published entrypoint;
- eventual símbolo privado
boot_done; - redefinição do ponto onde o frame lógico realmente termina;
- detecção de
init/framea partir de atributos de source; - remoção progressiva das configs explícitas atuais em
FrontendSpec.
Core Questions
- PBS deve introduzir
declare globalcomo declaração top-level distinta dedeclare const? declare globaldeve aceitar inicializadores executáveis comonew Vec2(...), calls e outras expressões lowerables?- O compiler deve sintetizar um module init por módulo que contenha
declare global? - A inicialização deve rodar eager no boot lógico do programa ou lazy no primeiro frame?
- Deve existir um
init()custom do usuário que roda depois de todos os module inits e antes do primeiroframe()? - O PBS deve introduzir atributos explícitos
[INIT]e[FRAME]para identificar as funções corretas do usuário? - A descoberta de entrypoint no frontend PBS deve deixar de depender de configuração explícita em
FrontendSpec? [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?- 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? - O published frame entrypoint deve passar a ser um wrapper sintético, por exemplo
__pbs_frame(), em vez doframe()do usuário? FRAME_RETdeve continuar significando "fim do frame lógico" e apenas mudar de owner, saindo da função do usuário para o wrapper sintético?- Como o compiler deve tratar ordem topológica, dependências e ciclos entre globals de módulos distintos?
- Qual política vale quando algum module init ou
init()do usuário falha: retry no próximo frame, fail-fast, ou outra forma? - Globals exportados por
mod.barreldevem 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 constnão é storage mutável/runtime-initialized;- o runtime já sabe executar um entrypoint published por frame;
FRAME_RETjá é 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 globalcom inicializadores realmente executáveis; - faz o PBS declarar na própria linguagem quais são os callables de
initeframe; - abre espaço para discutir de forma unificada
initpor módulo,initfinal e root de frame; - permite
init()custom do usuário sem mudar o runtime; - preserva o significado de
FRAME_RETcomo 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.
- resolve mal o caso motivador com
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:
- introduzir
declare globalcomo nova declaração top-level distinta dedeclare const; - permitir inicializadores executáveis lowerables, incluindo
newe calls compatíveis com o modelo de lowering executável; - sintetizar um module init por módulo owner de globals;
- ordenar os module inits de forma determinística e topológica;
- introduzir
[INIT]e[FRAME]como superfícies explícitas de detecção do init/frame do usuário; - 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; - discutir explicitamente se
[INIT]por módulo será uma surface do usuário ou se módulo continuará com init apenas sintético; - permitir um
init()custom opcional do usuário, executado depois de todos os module inits; - publicar um wrapper sintético de frame, por exemplo
__pbs_frame(), como verdadeiro frame root; - tratar
frame()do usuário como callable normal invocado por esse wrapper; - manter
FRAME_RETcomo marcador do fim do frame lógico, mas emitido no wrapper sintético publicado em vez de noframe()do usuário; - 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
- O nome surface deve ser exatamente
declare global, ou outra forma top-level? - Globals devem exigir initializer obrigatório em v1, ou existirão shells reservados parecidos com builtin const?
[INIT]e[FRAME]devem ser atributos reservados do PBS ou outra forma de marker surface?[INIT]deve exigir assinatura fixafn init() -> void?[FRAME]deve exigir assinatura fixafn frame() -> void?- Deve existir no máximo um
[INIT]e um[FRAME]por programa, por módulo owner, ou por outro escopo? [INIT]por módulo deve ser permitido como surface explícita do usuário ou ser non-goal da primeira fase?- Se
[INIT]por módulo existir, ele roda antes ou no lugar do module init sintético de globals? - A ausência de
[INIT]deve ser válida, mas a ausência de[FRAME]deve ser erro hard? - O
init()do usuário pertence apenas ao módulo owner do published frame entrypoint ou pode vir de qualquer módulo visível? - O compiler deve sintetizar
boot_donecomo global oculto de programa, local estático do wrapper, ou outro mecanismo interno? - 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? - Módulos podem ler globals de outros módulos durante init, desde que a ordem topológica já os tenha materializado?
- Como diagnosticar ciclos entre globals intra-módulo e inter-módulo de forma didática?
- 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? - O barrel/export de
globaldeve seguir a mesma superfície deconst, ou precisamos de distinção explícita na documentação/export metadata? - O
IRBackenddeve ganhar instruções explícitasGET_GLOBAL/SET_GLOBAL, ou globals devem ser lowered por outra representação intermediária antes de virar bytecode? - Em que etapa da migração a configuração explícita atual em
FrontendSpecdeixa de existir por completo?
Expected Decisions to Produce
- A surface language e a gramática de
declare global. - A surface language e a política de detecção de entrypoint via
[INIT]e[FRAME]. - O papel de
[INIT]por módulo versus module init totalmente sintético. - O contrato semântico de globals de módulo em PBS.
- O modelo de inicialização sintética por módulo.
- O contrato de
init()custom do usuário. - A semântica comportamental de
[FRAME]como root lógico do usuário. - A política de ordering e ciclo para init/global dependencies.
- O novo owner do
FRAME_RETno pipeline de frame. - O plano de remoção das configs explícitas atuais em
FrontendSpec. - 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
FrontendSpece 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:
- a forma de
declare global; - a superfície
[INIT]/[FRAME]e a nova detecção de entrypoints; - o modelo de module init sintético;
- o papel do
init()do usuário; - o published frame wrapper;
- o reposicionamento do
FRAME_RET; - a remoção da configuração explícita atual em
FrontendSpec.
Depois disso, abrir um pull-request/plan que separe explicitamente:
- parser/AST;
- semântica de atributos
[INIT]/[FRAME]e resolução de entrypoint; - semântica e barrel/export/import;
- IR/backend model para globals;
- synthesis de init e frame wrapper;
- remoção das configs explícitas antigas no frontend registry/specs;
- testes de regressão do pipeline compilador -> IR -> bytecode.