refactoring and reducing complexity

This commit is contained in:
bQUARKz 2026-03-10 09:14:44 +00:00
parent 1c38b19772
commit 14dce7ecc4
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 422 additions and 56 deletions

View File

@ -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`

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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);
}