17 KiB
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 globalsurface; - 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 constentra no value namespace;declare constcontinua reservado a valores imutáveis que não representam storage mutável de módulo;declare constnão materializa storage mutável runtime;- o initializer de
declare constpertence a um subset constante; - dependências de
declare constsão resolvidas por análise de dependência compile-time.
Ao mesmo tempo:
- a VM já expõe
GET_GLOBALeSET_GLOBAL; - a linguagem ainda não expõe globals mutáveis de módulo;
mod.barrelhoje já governa export/import deconst,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 constnão é storage mutável/runtime-initialized;declare constcontinua semanticamente separado dedeclare global;declare constcontinua 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
DependencyGraphAnaliseremutil.structuresna 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:
- a surface exata de
declare global; - a obrigatoriedade de initializer em todos os globals de v1;
- o namespace ocupado por globals;
- a política de visibility/barrel para globals;
- a identidade do storage owner;
- a semântica de import, export e alias;
- a política de dependência e ciclos entre globals.
Core Questions
- PBS deve introduzir exatamente
declare global name: T = expr;como nova forma top-level? declare globaldeve exigir type annotation explícita em todos os casos?- Quais formas de initializer executável são admissíveis para
declare globalem v1? declare globalentra no mesmo value namespace deletedeclare const?- O barrel deve ganhar uma entrada
globaldistinta? - Um
globalimportado referencia sempre o storage do módulo owner original? - Como imports de globals usam alias quando necessário sem introduzir shadowing?
- O que é proibido em imports de globals para evitar ambiguidade?
- A dependência entre globals deve ser modelada por grafo determinístico semelhante ao de
declare const, mas agora com efeito runtime? - Como ciclos entre globals intra-módulo e inter-módulo devem ser detectados e reportados?
- Como o grafo de globals mapeia para o
DependencyGraphAnalisercomum 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:
declare globaldeve ser uma declaração top-level distinta dedeclare const;- type annotation explícita deve ser obrigatória;
- initializer deve ser obrigatório em v1;
declare constnão deve ser reutilizado para globals e continua reservado a valores imutáveis;- globals devem entrar no value namespace, mas com categoria declarativa distinta;
- barrel deve refletir essa distinção explicitamente com
mod globalepub global; - PBS não deve introduzir reexport de globals nesta linha;
- imports de globals devem preservar a identidade do storage owner original e usar alias quando necessário;
- shadowing entre imports de
fn,service,globaleconstdeve ser compile-time error; - dependências entre globals devem ser determinísticas e diagnosticáveis.
Também fica registrado nesta agenda um direction técnico já decidido:
- modules e globals usarão o mesmo tipo estrutural de análise de dependência;
- isso deve nascer de um refactor do algoritmo atual de modules;
- o artefato comum deve viver na infra como
DependencyGraphAnaliseremutil.structures; - cada domínio continua responsável por construir seus próprios nós canônicos, suas arestas e seu mapeamento de diagnóstico;
- 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:
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.
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:
// 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:
- dependência local entre globals do mesmo módulo;
- dependência intermodular por import de global do módulo owner original;
- 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:
- ordem de inicialização deixa de poder depender de ordem textual do arquivo;
- imports com alias podem esconder o owner real do storage;
- leitura de global ainda não materializado precisa ser proibida por modelo, não tratada como comportamento oportunista;
- ciclos deixam de ser apenas "ciclos de expressão" e passam a ser ciclos de materialização runtime;
- 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:
- inicialização parcialmente observável: um global pode ler outro antes de ele existir semanticamente;
- ordem acidental por arquivo: a implementação pode acabar usando ordem textual ou ordem de carregamento do projeto, o que quebra determinismo;
- aliasing ambíguo: aliases de import podem parecer símbolo novo quando na verdade são só outro nome para o mesmo owner;
- ciclos opacos:
A -> B -> C -> Apode ficar escondido atrás de import e alias, dificultando explicação ao usuário; - bootstrap contaminado: se essa agenda deixar dependência frouxa, a 19.2 herda um lifecycle impossível de explicar;
- 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 é:
- toda leitura de outro global no initializer cria uma edge explícita no grafo de dependência;
- a resolução do grafo deve ser determinística e independente de source-file order;
- imports e aliases devem preservar o owner original do storage no grafo;
- ciclos entre globals devem ser erro estático, não comportamento runtime com retry ou valor parcial;
- 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:
- o algoritmo atual usado para dependência de modules deve ser refatorado em um componente estrutural reutilizável;
- esse componente comum deve se chamar
DependencyGraphAnaliser; - ele deve viver em
util.structuresna infra; - modules e globals devem consumir esse mesmo componente estrutural;
- 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:
- a surface correta é
declare global BLA: int = 1;; - type annotation é obrigatória, como em
declare const; - initializer é sempre obrigatório;
- globals entram no value namespace como categoria declarativa distinta;
mod.barreldeve expor globals com entradasmod global Name;epub global Name;;- imports de globals usam o mesmo esquema de alias quando necessário;
- PBS não introduz reexport de globals nesta linha;
- shadowing entre imports de
fn,service,globaleconsté compile-time error e deve ser resolvido com alias; - detecção de dependência, ordenação e ciclo deve nascer do
DependencyGraphAnalisercomum, com contexto semântico próprio de globals.
Também fica explicitado o modelo de namespace:
- globals entram no value namespace;
declare const,declare globale o singleton canônico deserviceconvivem no espaço nominal de valores;fn,service,globaleconstnão podem introduzir o mesmo nome visível no mesmo scope de resolução;- essa colisão é erro de compile time, mesmo quando a ambiguidade nasce de import;
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:
- init da declaração
global; - init por módulo;
- init de projeto.
Direção recomendada:
- o initializer de
declare globaldeve 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:
- 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; - 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;
- 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:
- calls para top-level
fnnão sejam permitidas em initializer deglobalem v1; - a necessidade de lógica mais rica seja absorvida pelo init de módulo discutido na
19.2; - 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:
some(...)enonenão façam parte do initializer declarativo deglobal;ifeswitchnão sejam permitidos diretamente na declaração deglobal.
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
fncall not allowed in global initializer; some(...)not allowed in global initializer;ifnot 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:
- init por módulo como fase procedural separada do initializer declarativo de
global; - init de projeto como fase distinta de coordenação final;
- permissão de loops em init de módulo e init de projeto;
- proibição de host calls por padrão durante init;
- permissão apenas para host calls marcadas com atributo reservado
[InitAllowed]; - validação compile-time dessa admissibilidade;
- 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.md4. Static Semantics Specification.md11. AST Specification.md12. Diagnostics Specification.md
Non-Goals
Esta agenda não deve:
- definir boot ordering;
- definir
[Init]ou[Frame]; - definir wrapper published entrypoint;
- definir posicionamento de
FRAME_RET; - 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