prometeu-studio/docs/compiler/pbs/agendas/19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda.md

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 global surface;
  • 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 const entra no value namespace;
  • declare const continua reservado a valores imutáveis que não representam storage mutável de módulo;
  • declare const não materializa storage mutável runtime;
  • o initializer de declare const pertence a um subset constante;
  • dependências de declare const são resolvidas por análise de dependência compile-time.

Ao mesmo tempo:

  • a VM já expõe GET_GLOBAL e SET_GLOBAL;
  • a linguagem ainda não expõe globals mutáveis de módulo;
  • mod.barrel hoje já governa export/import de const, 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 const não é storage mutável/runtime-initialized;
  • declare const continua semanticamente separado de declare global;
  • declare const continua 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 DependencyGraphAnaliser em util.structures na 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:

  1. a surface exata de declare global;
  2. a obrigatoriedade de initializer em todos os globals de v1;
  3. o namespace ocupado por globals;
  4. a política de visibility/barrel para globals;
  5. a identidade do storage owner;
  6. a semântica de import, export e alias;
  7. a política de dependência e ciclos entre globals.

Core Questions

  1. PBS deve introduzir exatamente declare global name: T = expr; como nova forma top-level?
  2. declare global deve exigir type annotation explícita em todos os casos?
  3. Quais formas de initializer executável são admissíveis para declare global em v1?
  4. declare global entra no mesmo value namespace de let e declare const?
  5. O barrel deve ganhar uma entrada global distinta?
  6. Um global importado referencia sempre o storage do módulo owner original?
  7. Como imports de globals usam alias quando necessário sem introduzir shadowing?
  8. O que é proibido em imports de globals para evitar ambiguidade?
  9. A dependência entre globals deve ser modelada por grafo determinístico semelhante ao de declare const, mas agora com efeito runtime?
  10. Como ciclos entre globals intra-módulo e inter-módulo devem ser detectados e reportados?
  11. Como o grafo de globals mapeia para o DependencyGraphAnaliser comum 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:

  1. declare global deve ser uma declaração top-level distinta de declare const;
  2. type annotation explícita deve ser obrigatória;
  3. initializer deve ser obrigatório em v1;
  4. declare const não deve ser reutilizado para globals e continua reservado a valores imutáveis;
  5. globals devem entrar no value namespace, mas com categoria declarativa distinta;
  6. barrel deve refletir essa distinção explicitamente com mod global e pub global;
  7. PBS não deve introduzir reexport de globals nesta linha;
  8. imports de globals devem preservar a identidade do storage owner original e usar alias quando necessário;
  9. shadowing entre imports de fn, service, global e const deve ser compile-time error;
  10. dependências entre globals devem ser determinísticas e diagnosticáveis.

Também fica registrado nesta agenda um direction técnico já decidido:

  1. modules e globals usarão o mesmo tipo estrutural de análise de dependência;
  2. isso deve nascer de um refactor do algoritmo atual de modules;
  3. o artefato comum deve viver na infra como DependencyGraphAnaliser em util.structures;
  4. cada domínio continua responsável por construir seus próprios nós canônicos, suas arestas e seu mapeamento de diagnóstico;
  5. 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:

  1. dependência local entre globals do mesmo módulo;
  2. dependência intermodular por import de global do módulo owner original;
  3. 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:

  1. ordem de inicialização deixa de poder depender de ordem textual do arquivo;
  2. imports com alias podem esconder o owner real do storage;
  3. leitura de global ainda não materializado precisa ser proibida por modelo, não tratada como comportamento oportunista;
  4. ciclos deixam de ser apenas "ciclos de expressão" e passam a ser ciclos de materialização runtime;
  5. 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:

  1. inicialização parcialmente observável: um global pode ler outro antes de ele existir semanticamente;
  2. ordem acidental por arquivo: a implementação pode acabar usando ordem textual ou ordem de carregamento do projeto, o que quebra determinismo;
  3. aliasing ambíguo: aliases de import podem parecer símbolo novo quando na verdade são só outro nome para o mesmo owner;
  4. ciclos opacos: A -> B -> C -> A pode ficar escondido atrás de import e alias, dificultando explicação ao usuário;
  5. bootstrap contaminado: se essa agenda deixar dependência frouxa, a 19.2 herda um lifecycle impossível de explicar;
  6. 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 é:

  1. toda leitura de outro global no initializer cria uma edge explícita no grafo de dependência;
  2. a resolução do grafo deve ser determinística e independente de source-file order;
  3. imports e aliases devem preservar o owner original do storage no grafo;
  4. ciclos entre globals devem ser erro estático, não comportamento runtime com retry ou valor parcial;
  5. 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:

  1. o algoritmo atual usado para dependência de modules deve ser refatorado em um componente estrutural reutilizável;
  2. esse componente comum deve se chamar DependencyGraphAnaliser;
  3. ele deve viver em util.structures na infra;
  4. modules e globals devem consumir esse mesmo componente estrutural;
  5. 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:

  1. a surface correta é declare global BLA: int = 1;;
  2. type annotation é obrigatória, como em declare const;
  3. initializer é sempre obrigatório;
  4. globals entram no value namespace como categoria declarativa distinta;
  5. mod.barrel deve expor globals com entradas mod global Name; e pub global Name;;
  6. imports de globals usam o mesmo esquema de alias quando necessário;
  7. PBS não introduz reexport de globals nesta linha;
  8. shadowing entre imports de fn, service, global e const é compile-time error e deve ser resolvido com alias;
  9. detecção de dependência, ordenação e ciclo deve nascer do DependencyGraphAnaliser comum, com contexto semântico próprio de globals.

Também fica explicitado o modelo de namespace:

  1. globals entram no value namespace;
  2. declare const, declare global e o singleton canônico de service convivem no espaço nominal de valores;
  3. fn, service, global e const não podem introduzir o mesmo nome visível no mesmo scope de resolução;
  4. essa colisão é erro de compile time, mesmo quando a ambiguidade nasce de import;
  5. 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:

  1. init da declaração global;
  2. init por módulo;
  3. init de projeto.

Direção recomendada:

  • o initializer de declare global deve 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:

  1. 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;
  2. 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;
  3. 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:

  1. calls para top-level fn não sejam permitidas em initializer de global em v1;
  2. a necessidade de lógica mais rica seja absorvida pelo init de módulo discutido na 19.2;
  3. 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:

  1. some(...) e none não façam parte do initializer declarativo de global;
  2. if e switch não sejam permitidos diretamente na declaração de global.

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 fn call not allowed in global initializer;
  • some(...) not allowed in global initializer;
  • if not 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:

  1. init por módulo como fase procedural separada do initializer declarativo de global;
  2. init de projeto como fase distinta de coordenação final;
  3. permissão de loops em init de módulo e init de projeto;
  4. proibição de host calls por padrão durante init;
  5. permissão apenas para host calls marcadas com atributo reservado [InitAllowed];
  6. validação compile-time dessa admissibilidade;
  7. 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.md
  • 4. Static Semantics Specification.md
  • 11. AST Specification.md
  • 12. Diagnostics Specification.md

Non-Goals

Esta agenda não deve:

  1. definir boot ordering;
  2. definir [Init] ou [Frame];
  3. definir wrapper published entrypoint;
  4. definir posicionamento de FRAME_RET;
  5. 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