From 14dce7ecc475757226e17146ca38a317e5f4a845 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 10 Mar 2026 09:14:44 +0000 Subject: [PATCH] refactoring and reducing complexity --- docs/pbs/pull-requests/INDEX.md | 20 ++++ ...nalyzer-context-and-dispatch-foundation.md | 69 +++++++++++++ ...ral-and-structural-expression-splitting.md | 71 ++++++++++++++ ...all-and-member-resolution-decomposition.md | 72 ++++++++++++++ ...nalyzer-switch-and-handle-decomposition.md | 73 ++++++++++++++ ...-regression-hardening-and-final-cleanup.md | 77 +++++++++++++++ .../pbs/semantics/PbsFlowBodyAnalyzer.java | 96 ++++++++----------- 7 files changed, 422 insertions(+), 56 deletions(-) create mode 100644 docs/pbs/pull-requests/PR-12.1-flow-expression-analyzer-context-and-dispatch-foundation.md create mode 100644 docs/pbs/pull-requests/PR-12.2-flow-expression-analyzer-literal-and-structural-expression-splitting.md create mode 100644 docs/pbs/pull-requests/PR-12.3-flow-expression-analyzer-call-and-member-resolution-decomposition.md create mode 100644 docs/pbs/pull-requests/PR-12.4-flow-expression-analyzer-switch-and-handle-decomposition.md create mode 100644 docs/pbs/pull-requests/PR-12.5-flow-expression-analyzer-regression-hardening-and-final-cleanup.md diff --git a/docs/pbs/pull-requests/INDEX.md b/docs/pbs/pull-requests/INDEX.md index 281de03f..44ada33b 100644 --- a/docs/pbs/pull-requests/INDEX.md +++ b/docs/pbs/pull-requests/INDEX.md @@ -175,3 +175,23 @@ Foco: reduzir complexidade estrutural de `PbsExprParser` sem alterar precedence, 3. `PR-11.3-pbs-expr-parser-postfix-primary-and-construction-decomposition.md` 4. `PR-11.4-pbs-expr-parser-literals-patterns-and-shared-token-utilities.md` 5. `PR-11.5-pbs-expr-parser-regression-hardening-and-final-cleanup.md` + +### Onda O12 - Flow Expression Analyzer Maintainability Refactor (No Functional Change) + +Foco: reduzir complexidade estrutural de `PbsFlowExpressionAnalyzer` sem alterar inferencia, diagnosticos, matching de tipos ou comportamento observavel da analise semantica. + +1. `PR-12.1-flow-expression-analyzer-context-and-dispatch-foundation.md` +2. `PR-12.2-flow-expression-analyzer-literal-and-structural-expression-splitting.md` +3. `PR-12.3-flow-expression-analyzer-call-and-member-resolution-decomposition.md` +4. `PR-12.4-flow-expression-analyzer-switch-and-handle-decomposition.md` +5. `PR-12.5-flow-expression-analyzer-regression-hardening-and-final-cleanup.md` + +### Onda O13 - Flow Body Analyzer Maintainability Refactor (No Functional Change) + +Foco: reduzir complexidade estrutural de `PbsFlowBodyAnalyzer` sem alterar regras de flow, completion, assignment checking ou diagnosticos observaveis. + +1. `PR-13.1-flow-body-analyzer-context-and-entrypoint-foundation.md` +2. `PR-13.2-flow-body-analyzer-callable-body-and-completion-decomposition.md` +3. `PR-13.3-flow-body-analyzer-statement-analysis-splitting.md` +4. `PR-13.4-flow-body-analyzer-assignment-target-resolution-decomposition.md` +5. `PR-13.5-flow-body-analyzer-regression-hardening-and-final-cleanup.md` diff --git a/docs/pbs/pull-requests/PR-12.1-flow-expression-analyzer-context-and-dispatch-foundation.md b/docs/pbs/pull-requests/PR-12.1-flow-expression-analyzer-context-and-dispatch-foundation.md new file mode 100644 index 00000000..cad45365 --- /dev/null +++ b/docs/pbs/pull-requests/PR-12.1-flow-expression-analyzer-context-and-dispatch-foundation.md @@ -0,0 +1,69 @@ +# PR-12.1 - Flow Expression Analyzer Context and Dispatch Foundation + +## Briefing + +`PbsFlowExpressionAnalyzer` concentra estado compartilhado e um dispatch grande de `Expression -> semantic analysis` em uma unica classe. + +Esta PR introduz a base estrutural para decomposicao sem alterar funcionalidade: contexto compartilhado, helpers comuns e uma fronteira clara entre fachada e implementacoes especializadas. + +## Motivation + +### Dor atual que esta PR resolve + +1. A classe combina orquestracao, recursion, block delegation e regras semanticas detalhadas. +2. Extracoes futuras ficam arriscadas sem um contexto compartilhado explicito. +3. O maior hotspot da classe e o dispatch principal de `analyzeExpressionInternal`, que hoje depende de muitos parametros repetidos. + +## Target + +Estabelecer uma fundacao segura para o refactor: + +1. contexto compartilhado para analise de expressao, +2. encapsulamento de argumentos recorrentes, +3. fachada pequena em `PbsFlowExpressionAnalyzer`. + +## Dependencies + +Nenhuma. + +## Scope + +1. Introduzir um contexto/request object para `scope`, `expectedType`, `returnType`, `resultErrorName`, `receiverType`, `model`, `diagnostics`, `use` e `valueContext`. +2. Encapsular a dependencia de `BlockAnalyzer` nessa camada. +3. Reorganizar o dispatch principal sem mover ainda regras complexas para outras classes. + +## Non-Goals + +1. Nao alterar inferencia de tipos. +2. Nao alterar diagnosticos ou spans. +3. Nao reescrever `PbsFlowTypeOps` ou `PbsFlowBodyAnalyzer`. + +## Method + +### O que deve ser feito explicitamente + +1. Criar um contexto pequeno e explicito para a analise recursiva. +2. Manter `PbsFlowExpressionAnalyzer` como entrypoint package-private. +3. Preservar a ordem atual de recursion e emissao de diagnosticos. +4. Garantir que esta PR seja puramente estrutural. + +## Acceptance Criteria + +1. `PbsFlowExpressionAnalyzer` continua com o mesmo contrato observavel. +2. A quantidade de parametros repetidos no dispatch recursivo e reduzida. +3. Nao ha mudanca funcional em inferencia, erros ou comportamento da suite. + +## Tests + +1. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsDeclarationsTest*` +2. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsAssignmentTest*` +3. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsApplyResolutionTest*` +4. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test` + +## Affected Documents + +1. Nenhum documento normativo. + +## Open Questions + +1. O contexto deve ser imutavel por chamada recursiva ou permitir derivacao incremental. A recomendacao e usar derivacao imutavel leve para reduzir risco de regressao. diff --git a/docs/pbs/pull-requests/PR-12.2-flow-expression-analyzer-literal-and-structural-expression-splitting.md b/docs/pbs/pull-requests/PR-12.2-flow-expression-analyzer-literal-and-structural-expression-splitting.md new file mode 100644 index 00000000..f86ff174 --- /dev/null +++ b/docs/pbs/pull-requests/PR-12.2-flow-expression-analyzer-literal-and-structural-expression-splitting.md @@ -0,0 +1,71 @@ +# PR-12.2 - Flow Expression Analyzer Literal and Structural Expression Splitting + +## Briefing + +Grande parte do dispatch de `PbsFlowExpressionAnalyzer` cobre formas literais e estruturais de expressao: literais, `this`, identificadores, grupos, tuplas, blocos, unarios, binarios, `none/some`, `new`, `as`, `ok/err`. + +Esta PR extrai essas familias para componentes menores, sem alterar funcionalidade. + +## Motivation + +### Dor atual que esta PR resolve + +1. O dispatch principal mistura casos triviais com regras mais complexas de call/member/switch/handle. +2. Formas estruturais simples podem ser separadas com risco baixo e alta reducao de complexidade. +3. Sem esse corte, o dispatch central continua inchado mesmo com contexto compartilhado. + +## Target + +Separar a analise de expressoes literais e estruturais: + +1. literais e identificadores, +2. grupos, tuplas e blocos, +3. unario/binario, +4. `none/some`, `new`, `bind`-independent simple forms, `as`, `ok/err`. + +## Dependencies + +Prerequisito direto: + +1. `PR-12.1` + +## Scope + +1. Extrair casos simples de `analyzeExpressionInternal` para helpers/colaboradores dedicados. +2. Manter o resultado em `ExprResult` exatamente como hoje. +3. Preservar todas as chamadas a `typeOps` e `blockAnalyzer`. + +## Non-Goals + +1. Nao alterar regras de callback/bind. +2. Nao alterar member access ou overload resolution. +3. Nao alterar switch/handle/result propagation. + +## Method + +### O que deve ser feito explicitamente + +1. Mover primeiro os casos com menor acoplamento semantico. +2. Manter o dispatch principal apenas como roteador. +3. Preservar a ordem atual de recursion para evitar mudanca em diagnosticos acumulados. + +## Acceptance Criteria + +1. Casos literais e estruturais deixam de ficar todos dentro do dispatch principal. +2. AST/semantics observados pela suite permanecem equivalentes. +3. O hotspot principal da classe reduz tamanho e branching sem alterar comportamento. + +## Tests + +1. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsExprParserTest*` +2. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsOptionalResultTest*` +3. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsDeclarationsTest*` +4. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test` + +## Affected Documents + +1. Nenhum documento normativo. + +## Open Questions + +1. `BindExpr` deve entrar aqui ou junto de call resolution. A recomendacao e mantê-lo para `PR-12.3`, porque ele depende de matching de callback/callable. diff --git a/docs/pbs/pull-requests/PR-12.3-flow-expression-analyzer-call-and-member-resolution-decomposition.md b/docs/pbs/pull-requests/PR-12.3-flow-expression-analyzer-call-and-member-resolution-decomposition.md new file mode 100644 index 00000000..f53324e0 --- /dev/null +++ b/docs/pbs/pull-requests/PR-12.3-flow-expression-analyzer-call-and-member-resolution-decomposition.md @@ -0,0 +1,72 @@ +# PR-12.3 - Flow Expression Analyzer Call and Member Resolution Decomposition + +## Briefing + +`analyzeCallExpression`, `analyzeApplyExpression`, `resolveCallableApplication`, `analyzeMemberExpression` e parte de `BindExpr` concentram a logica de resolucao mais sensivel do analisador. + +Esta PR extrai essa superficie para colaboradores menores, sem alterar regras de overload, bare method extraction, acesso a campos ou matching de callback. + +## Motivation + +### Dor atual que esta PR resolve + +1. Member access e callable resolution sao os clusters mais densos depois do dispatch principal. +2. A classe mistura regras de acesso a tuple/struct/service/contract com overload resolution e bind matching. +3. Qualquer ajuste nessas regras hoje exige tocar um arquivo muito central e com alta chance de regressao. + +## Target + +Separar a analise de resolucao: + +1. member access, +2. call/apply overload resolution, +3. bind-to-callback matching, +4. field access permission checks. + +## Dependencies + +Prerequisitos diretos: + +1. `PR-12.1` +2. `PR-12.2` + +## Scope + +1. Extrair `analyzeCallExpression`, `analyzeApplyExpression`, `resolveCallableApplication`. +2. Extrair `analyzeMemberExpression` e `canReadStructField`. +3. Mover a parte especifica de `BindExpr` para o mesmo cluster de resolucao. + +## Non-Goals + +1. Nao alterar diagnosticos de overload ambiguous/unresolved. +2. Nao alterar regras de leitura de campo privado/publico. +3. Nao alterar a proibicao de bare method extraction. + +## Method + +### O que deve ser feito explicitamente + +1. Criar um resolver focado em call/member semantics. +2. Preservar o mesmo uso de `TypeView`, `CallableSymbol` e `ExprResult`. +3. Garantir que o order of checks permaneça equivalente, especialmente em erros e narrowing por `expectedType`. + +## Acceptance Criteria + +1. Call/member resolution deixa de ficar concentrada em `PbsFlowExpressionAnalyzer`. +2. Bare method extraction, field access e overload matching permanecem equivalentes. +3. Suite semantica continua verde sem mudancas funcionais. + +## Tests + +1. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsApplyResolutionTest*` +2. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsAssignmentTest*` +3. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsFrontendCompilerTest*` +4. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test` + +## Affected Documents + +1. Nenhum documento normativo. + +## Open Questions + +1. `BindExpr` deve compartilhar exatamente o mesmo narrowing path de `call/apply` ou ficar em helper proprio. A recomendacao e helper proprio dentro do mesmo resolver, para evitar acoplamento artificial. diff --git a/docs/pbs/pull-requests/PR-12.4-flow-expression-analyzer-switch-and-handle-decomposition.md b/docs/pbs/pull-requests/PR-12.4-flow-expression-analyzer-switch-and-handle-decomposition.md new file mode 100644 index 00000000..923e7bbc --- /dev/null +++ b/docs/pbs/pull-requests/PR-12.4-flow-expression-analyzer-switch-and-handle-decomposition.md @@ -0,0 +1,73 @@ +# PR-12.4 - Flow Expression Analyzer Switch and Handle Decomposition + +## Briefing + +`switch` e `handle` sao as maiores superfices especializadas restantes em `PbsFlowExpressionAnalyzer`, com regras de exaustividade, pattern typing, matching de erro e arm typing. + +Esta PR separa essas construcoes em analisadores dedicados, mantendo exatamente o comportamento atual. + +## Motivation + +### Dor atual que esta PR resolve + +1. `analyzeSwitchExpression` e `analyzeHandleExpression` concentram regras de alto branching e recovery semantico. +2. Essas regras sao semanticamente coesas e merecem fronteiras proprias. +3. Sem esse corte, a classe principal continua com os hotspots mais caros de manutencao. + +## Target + +Extrair a analise especializada de: + +1. `switch expression`, +2. `switch pattern typing/keying`, +3. `handle expression`, +4. `handle block arm` e matching de error targets. + +## Dependencies + +Prerequisitos diretos: + +1. `PR-12.1` +2. `PR-12.2` +3. `PR-12.3` + +## Scope + +1. Extrair `analyzeSwitchExpression`, `switchPatternKey`, `switchPatternType`. +2. Extrair `analyzeHandleExpression`, `analyzeHandleBlockArm`, `matchesTargetError`, `unwrapGroup`. +3. Preservar a interacao com `blockAnalyzer`, `typeOps` e `model`. + +## Non-Goals + +1. Nao alterar regras de exaustividade de switch em value position. +2. Nao alterar regras de wildcard/default duplication. +3. Nao alterar regras de `handle` para `ok(...)` / `err(...)` terminais. + +## Method + +### O que deve ser feito explicitamente + +1. Criar analisadores especializados por construcao. +2. Preservar ordem de validacoes e diagnosticos. +3. Reusar o contexto compartilhado para evitar nova explosao de parametros. + +## Acceptance Criteria + +1. `switch` e `handle` deixam de ficar implementados diretamente na classe principal. +2. Exaustividade, arm typing e error matching permanecem equivalentes. +3. Nenhum teste de result flow, control flow ou optional/result muda comportamento esperado. + +## Tests + +1. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsResultFlowRulesTest*` +2. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsOptionalResultTest*` +3. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsControlFlowTest*` +4. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test` + +## Affected Documents + +1. Nenhum documento normativo. + +## Open Questions + +1. `switchPatternKey` e `switchPatternType` devem ficar juntos no mesmo colaborador. A recomendacao e sim, porque ambos definem a semantica da superficie de pattern. diff --git a/docs/pbs/pull-requests/PR-12.5-flow-expression-analyzer-regression-hardening-and-final-cleanup.md b/docs/pbs/pull-requests/PR-12.5-flow-expression-analyzer-regression-hardening-and-final-cleanup.md new file mode 100644 index 00000000..642ddb84 --- /dev/null +++ b/docs/pbs/pull-requests/PR-12.5-flow-expression-analyzer-regression-hardening-and-final-cleanup.md @@ -0,0 +1,77 @@ +# PR-12.5 - Flow Expression Analyzer Regression Hardening and Final Cleanup + +## Briefing + +As PRs anteriores reduzem a complexidade estrutural de `PbsFlowExpressionAnalyzer`. Esta PR fecha a trilha com consolidacao final, limpeza de duplicacao residual e endurecimento de regressao, sem alterar funcionalidade. + +## Motivation + +### Dor atual que esta PR resolve + +1. Refactors estruturais em analise semantica podem preservar casos principais e ainda mudar detalhes finos de diagnostico. +2. Ao final da decomposicao, tendem a restar helpers redundantes ou caminhos duplicados. +3. O fechamento da trilha precisa garantir equivalencia observavel e uma fachada final pequena. + +## Target + +Concluir a trilha de refactor: + +1. limpeza estrutural final, +2. consolidacao dos helpers remanescentes, +3. cobertura de regressao para inferencia e diagnosticos. + +## Dependencies + +Prerequisitos diretos: + +1. `PR-12.1` +2. `PR-12.2` +3. `PR-12.3` +4. `PR-12.4` + +## Scope + +1. Remover duplicacao residual apos as extracoes. +2. Garantir que `PbsFlowExpressionAnalyzer` fique reduzido a composicao/orquestracao. +3. Reforcar testes de regressao para call/member resolution, result flow, switch e handle. + +## Non-Goals + +1. Nao alterar taxonomia de `PbsSemanticsErrors`. +2. Nao alterar `TypeView`, `ExprResult` ou `PbsFlowTypeOps`. +3. Nao introduzir novas regras semanticas. + +## Method + +### O que deve ser feito explicitamente + +1. Fazer uma passada final de consolidacao. +2. Verificar equivalencia com foco em: + - inferencia de tipos, + - overload resolution, + - access checks, + - switch/handle exhaustiveness e matching, + - diagnosticos emitidos. +3. Encerrar a trilha com uma fachada pequena e clara. + +## Acceptance Criteria + +1. `PbsFlowExpressionAnalyzer` termina como orquestrador, nao como concentrador de toda a logica. +2. Nao ha mudanca funcional observavel em analise, tipos inferidos ou diagnosticos. +3. Suite do frontend PBS passa integralmente. + +## Tests + +1. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsApplyResolutionTest*` +2. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsResultFlowRulesTest*` +3. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsOptionalResultTest*` +4. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test --tests *PbsSemanticsControlFlowTest*` +5. `:prometeu-compiler:frontends:prometeu-frontend-pbs:test` + +## Affected Documents + +1. Nenhum documento normativo. + +## Open Questions + +1. Nenhuma. A partir desta PR, qualquer mudanca adicional deve ser tratada como evolucao funcional separada. diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBodyAnalyzer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBodyAnalyzer.java index 1b8b137c..aa635cbf 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBodyAnalyzer.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBodyAnalyzer.java @@ -1,7 +1,9 @@ package p.studio.compiler.pbs.semantics; import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.compiler.source.Span; import p.studio.compiler.source.diagnostics.DiagnosticSink; +import p.studio.compiler.source.diagnostics.Diagnostics; import p.studio.utilities.structures.ReadOnlyList; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind; @@ -20,10 +22,6 @@ final class PbsFlowBodyAnalyzer { boolean assignable) { } - public void validate(final PbsAst.File ast, final DiagnosticSink diagnostics) { - validate(ast, ReadOnlyList.empty(), diagnostics); - } - public void validate( final PbsAst.File ast, final ReadOnlyList supplementalTopDecls, @@ -118,14 +116,14 @@ final class PbsFlowBodyAnalyzer { } if (enforceResultCompletion) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_POSSIBLE_FALLTHROUGH_RESULT.name(), "Possible fallthrough in result callable body; all paths must return explicitly", body.span()); return; } - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_POSSIBLE_FALLTHROUGH_NON_UNIT.name(), "Possible fallthrough in plain non-unit callable body; all paths must return explicitly", body.span()); @@ -171,26 +169,15 @@ final class PbsFlowBodyAnalyzer { private boolean expressionAlwaysReturns( final PbsAst.Expression expression, final Model model) { - if (expression == null) { - return false; - } - if (expression instanceof PbsAst.GroupExpr groupExpr) { - return expressionAlwaysReturns(groupExpr.expression(), model); - } - if (expression instanceof PbsAst.BlockExpr blockExpr) { - return blockAlwaysReturns(blockExpr.block(), model); - } - if (expression instanceof PbsAst.IfExpr ifExpr) { - return blockAlwaysReturns(ifExpr.thenBlock(), model) + return switch (expression) { + case PbsAst.GroupExpr groupExpr -> expressionAlwaysReturns(groupExpr.expression(), model); + case PbsAst.BlockExpr blockExpr -> blockAlwaysReturns(blockExpr.block(), model); + case PbsAst.IfExpr ifExpr -> blockAlwaysReturns(ifExpr.thenBlock(), model) && expressionAlwaysReturns(ifExpr.elseExpression(), model); - } - if (expression instanceof PbsAst.SwitchExpr switchExpr) { - return switchAlwaysReturns(switchExpr, model); - } - if (expression instanceof PbsAst.HandleExpr handleExpr) { - return handleAlwaysReturns(handleExpr, model); - } - return false; + case PbsAst.SwitchExpr switchExpr -> switchAlwaysReturns(switchExpr, model); + case PbsAst.HandleExpr handleExpr -> handleAlwaysReturns(handleExpr, model); + case null, default -> false; + }; } private boolean handleAlwaysReturns( @@ -373,7 +360,7 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); if (!typeOps.isBool(condition)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(), "If statement condition must have bool type", ifStatement.condition().span()); @@ -390,7 +377,7 @@ final class PbsFlowBodyAnalyzer { if (statement instanceof PbsAst.ForStatement( String iteratorName, PbsAst.TypeRef type, PbsAst.Expression fromExpression, PbsAst.Expression untilExpression, PbsAst.Expression stepExpression, PbsAst.Block body, - p.studio.compiler.source.Span span + Span span )) { final var iteratorType = typeOps.typeFromTypeRef(type, model, receiverType); final var fromType = expressionAnalyzer.analyzeExpression( @@ -418,7 +405,7 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); if (!typeOps.compatible(fromType, iteratorType) || !typeOps.compatible(untilType, iteratorType)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_FOR_TYPE_MISMATCH.name(), "For-loop bounds must match declared iterator type", span); @@ -437,7 +424,7 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); if (!typeOps.compatible(stepType, iteratorType)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_FOR_TYPE_MISMATCH.name(), "For-loop step must match declared iterator type", stepExpression.span()); @@ -462,7 +449,7 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); if (!typeOps.isBool(condition)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_WHILE_NON_BOOL_CONDITION.name(), "While condition must have bool type", whileStatement.condition().span()); @@ -529,8 +516,8 @@ final class PbsFlowBodyAnalyzer { final TypeView receiverType, final Model model, final DiagnosticSink diagnostics) { - if (returnType.kind() != PbsFlowSemanticSupport.Kind.RESULT || resultErrorName == null) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + if (returnType.kind() != Kind.RESULT || resultErrorName == null) { + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(), "'ok(...)' is only allowed when returning from a result callable", okExpr.span()); @@ -563,7 +550,7 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); if (!typeOps.compatible(actualType, payloadType)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_RESULT_OK_PAYLOAD_MISMATCH.name(), "Payload in 'ok(...)' is incompatible with result payload type", okExpr.value().span()); @@ -576,8 +563,8 @@ final class PbsFlowBodyAnalyzer { final String resultErrorName, final Model model, final DiagnosticSink diagnostics) { - if (returnType.kind() != PbsFlowSemanticSupport.Kind.RESULT || resultErrorName == null) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + if (returnType.kind() != Kind.RESULT || resultErrorName == null) { + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(), "'err(...)' is only allowed when returning from a result callable", errExpr.span()); @@ -585,7 +572,7 @@ final class PbsFlowBodyAnalyzer { } if (!matchesTargetError(errExpr.errorPath(), resultErrorName, model)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_RESULT_ERROR_LABEL_INVALID.name(), "Error label in 'err(...)' does not match enclosing result error type", errExpr.errorPath().span()); @@ -621,7 +608,7 @@ final class PbsFlowBodyAnalyzer { final Model model, final DiagnosticSink diagnostics) { final var target = resolveAssignmentTarget(assignStatement.target(), scope, receiverType, model, diagnostics); - final var expectedType = target == null ? null : target.type(); + final var expectedType = target.type(); final var valueType = expressionAnalyzer.analyzeExpression( assignStatement.value(), scope, @@ -635,12 +622,12 @@ final class PbsFlowBodyAnalyzer { true, this::analyzeBlock).type(); - if (target == null || !target.assignable()) { + if (!target.assignable()) { return; } if (!assignmentCompatible(assignStatement.operator(), target.type(), valueType)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TYPE_MISMATCH.name(), "Assigned value type is not compatible with assignment target", assignStatement.span()); @@ -658,12 +645,12 @@ final class PbsFlowBodyAnalyzer { if (lValue.pathSegments().isEmpty()) { if (rootIsThis) { if (receiverType == null) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_INVALID_THIS_CONTEXT.name(), "Invalid 'this' usage outside struct/service methods and constructors", lValue.span()); } - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TARGET_NOT_ASSIGNABLE.name(), "Assignment target is not assignable", lValue.span()); @@ -673,7 +660,7 @@ final class PbsFlowBodyAnalyzer { final var localType = scope.resolve(rootName); if (localType != null) { if (!scope.isMutable(rootName)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TARGET_NOT_ASSIGNABLE.name(), "Cannot assign to immutable local '%s'".formatted(rootName), lValue.span()); @@ -683,14 +670,14 @@ final class PbsFlowBodyAnalyzer { } if (isKnownNonAssignableRoot(rootName, model)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TARGET_NOT_ASSIGNABLE.name(), "Assignment target is not assignable", lValue.span()); return new AssignmentTargetResolution(TypeView.unknown(), false); } - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TARGET_UNRESOLVED.name(), "Assignment target '%s' does not resolve".formatted(rootName), lValue.span()); @@ -701,7 +688,7 @@ final class PbsFlowBodyAnalyzer { var currentReceiverIsThis = rootIsThis; if (rootIsThis) { if (receiverType == null) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_INVALID_THIS_CONTEXT.name(), "Invalid 'this' usage outside struct/service methods and constructors", lValue.span()); @@ -717,7 +704,7 @@ final class PbsFlowBodyAnalyzer { } else if (model.constTypes.containsKey(rootName)) { currentType = model.constTypes.get(rootName); } else { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_ASSIGN_TARGET_UNRESOLVED.name(), "Assignment target '%s' does not resolve".formatted(rootName), lValue.span()); @@ -728,8 +715,8 @@ final class PbsFlowBodyAnalyzer { for (int i = 0; i < lValue.pathSegments().size(); i++) { final var segment = lValue.pathSegments().get(i); final var isLastSegment = i == lValue.pathSegments().size() - 1; - if (currentType.kind() != PbsFlowSemanticSupport.Kind.STRUCT) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + if (currentType.kind() != Kind.STRUCT) { + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(), "Assignment target segment '%s' is not a struct field".formatted(segment), lValue.span()); @@ -742,7 +729,7 @@ final class PbsFlowBodyAnalyzer { } final var field = struct.fields().get(segment); if (field == null) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(), "Struct member '%s' does not exist".formatted(segment), lValue.span()); @@ -750,11 +737,11 @@ final class PbsFlowBodyAnalyzer { } final var ownerContext = receiverType != null - && receiverType.kind() == PbsFlowSemanticSupport.Kind.STRUCT + && receiverType.kind() == Kind.STRUCT && receiverType.name().equals(currentType.name()); if (isLastSegment) { if (!canWriteStructField(field, ownerContext, currentReceiverIsThis)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_FIELD_WRITE_ACCESS_DENIED.name(), "Write access to struct field '%s' is not permitted".formatted(segment), lValue.span()); @@ -764,7 +751,7 @@ final class PbsFlowBodyAnalyzer { } if (!canReadStructField(field, ownerContext, currentReceiverIsThis)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + Diagnostics.error(diagnostics, PbsSemanticsErrors.E_SEM_FIELD_READ_ACCESS_DENIED.name(), "Read access to struct field '%s' is not permitted".formatted(segment), lValue.span()); @@ -792,11 +779,8 @@ final class PbsFlowBodyAnalyzer { case MUL_ASSIGN -> "*"; case DIV_ASSIGN -> "/"; case MOD_ASSIGN -> "%"; - case ASSIGN -> "="; + default -> throw new IllegalStateException("Unexpected value: " + operator); }; - if ("=".equals(binaryOperator)) { - return typeOps.compatible(valueType, targetType); - } final var resultType = typeOps.inferBinaryResult(binaryOperator, targetType, valueType); return typeOps.compatible(resultType, targetType); }