Cópia estática
Copiado de docs/decisions/surface-decisions.md em https://github.com/poppy-team/aipo-lang (MIT). Fixado na revisão ce47c4e070404fc41722ccd8e2b7b808b99062a8, blob 17ad2cf64dd8b48804fecb930f1594d62ceed70a. O repositório de origem permanece canônico; esta cópia é atualizada por um pull request de sincronização, não em tempo real.
Registro vivo das decisões de superfície da linguagem Aipo, para não perder contexto entre conversas. Opção rejeitada permanece registrada aqui de propósito: uma conversa futura pode reabri-la sem reconstruir o histórico.
expr? (propagação de Failure) let v = f(x)? — posfixo, liga mais forte que todo operador binário.Failure, propaga para o attempt mais próximo (ou para o caller quando não há handler); caso contrário, entrega o valor.await, que é restrito a statement, inicializador e valor de retorno (AIPO_SEM_AWAIT_IN_SUBEXPRESSION).PropagateFailure para CheckFailure (OpCode), que é exatamente a checagem que a VM já executa em todo limite de statement. Nenhuma instrução nova de VM.docs/conformance/programs/29_try_propagation_operator.{aipo,stdout}.crates/aipo-cli/tests/try_operator_tests.rs (3 verdes).expr with { ... } (atualização funcional de struct) let novo = p with { y: 99 } — Opção A, palavra-chave with + chaves.p permanece intacto. Campos ausentes no bloco são copiados do original.?.Construct, o que reaproveita a validação de campos de struct já existente no sema.Point(p, y: 99) — sem palavra-chave nova; reusa Construct com o struct como primeiro argumento.
Construct (passa a aceitar um valor base antes do brace); pior legabilidade em aninhamento (Point(a, x: Point(b, x: 1))).with sair do escopo e a atualização de struct virar caso raro o bastante para não justificar palavra-chave. Reabrir exige revisar a ambiguidade com Construct posicional.p with y: 99, sem chaves): as chaves delimitam o bloco de mudanças e mantêm a leitura localmente previsível. Só difere de A por ruído visual; sem ganho.match when { nome, idade }): sem repetir o tipo, porque a extração segue a mesma garantia do acesso por ponto — um campo ausente é TypeMismatch em runtime, e a tipagem que C (narrowing) trouxer só torna o erro mais cedo. Opção B (when Point { nome, idade }) foi considerada e dispensada por ruído; quando os arms misturam structs diferentes, o fallthrough natural já seleciona o primeiro que casa.when pattern if guard then): reusa if, zero palavra nova. Um guard false cai para o próximo arm, não para o else — é um filtro, não um desvio.check_condition_bool aplica, mesma regra de if/while.Load target → GetField → Store antes do corpo (bindings estabelecidos antes do guard, para o guard testar os campos); guard vira JumpIfFalse para o fail-jump do arm. Destructure emite Jump incondicional para o corpo (não pode falhar) — esse salto faltou no primeiro build e deixava o arm inalcançável.UnsupportedStmt explícito, em vez de compilar errado em silêncio (mesmo padrão de Try, que cai no catch-all).Language Reference (l. 929) e Especificação Viva (l. 1368) colocam guards e destructuring fora da V1. A.3 é extensão deliberada; os docs precisam de uma seção fechando a mudança antes do release.docs/conformance/programs/31_match_destructure_and_guards.{aipo,stdout}.crates/aipo-cli/tests/match_destructure_tests.rs (6 verdes: bind, guards aceita/rejeita, subconjunto, guard em value pattern, escopo do arm, guard não-Bool).enum) docs/conformance/programs/17_unicode_nfc.aipo falhava: o folder de constantes do IR concatenava strings com format! sem normalizar, enquanto o Add da VM compõe NFC. Como aipo run usa optimize = true por padrão, literais inline ("e" + "\u{0301}") eram dobrados sem normalizar. Corrigido em crates/aipo-ir/src/opt.rs (o folder normaliza igual ao runtime), com teste de regressão test_folds_string_concatenation_to_nfc. Suíte de conformidade verde pela primeira vez (3/3 test_programs*).