refactoring and reducing complexity
This commit is contained in:
parent
1c38b19772
commit
14dce7ecc4
@ -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`
|
||||
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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<PbsAst.TopDecl> 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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user