implements PR-12.5

This commit is contained in:
bQUARKz 2026-03-10 10:22:26 +00:00
parent dd598b3989
commit df0d9f4601
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 126 additions and 93 deletions

View File

@ -0,0 +1,118 @@
package p.studio.compiler.pbs.semantics;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.source.diagnostics.Diagnostics;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
final class PbsFlowBranchExpressionAnalyzer {
@FunctionalInterface
interface ExpressionAnalyzerDelegate {
ExprResult analyze(PbsAst.Expression expression, PbsFlowExpressionContext context);
}
@FunctionalInterface
interface BlockAnalysisDelegate {
TypeView analyze(PbsAst.Block block, PbsFlowExpressionContext context, boolean valueContext);
}
private final PbsFlowTypeOps typeOps;
private final ExpressionAnalyzerDelegate expressionAnalyzerDelegate;
private final BlockAnalysisDelegate blockAnalysisDelegate;
PbsFlowBranchExpressionAnalyzer(
final PbsFlowTypeOps typeOps,
final ExpressionAnalyzerDelegate expressionAnalyzerDelegate,
final BlockAnalysisDelegate blockAnalysisDelegate) {
this.typeOps = typeOps;
this.expressionAnalyzerDelegate = expressionAnalyzerDelegate;
this.blockAnalysisDelegate = blockAnalysisDelegate;
}
ExprResult analyzeIfExpression(
final PbsAst.IfExpr ifExpr,
final PbsFlowExpressionContext context) {
final var diagnostics = context.diagnostics();
final var expectedType = context.expectedType();
final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type();
if (!typeOps.isBool(condition)) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(),
"If expression condition must have bool type",
ifExpr.condition().span());
}
final var thenType = blockAnalysisDelegate.analyze(ifExpr.thenBlock(), context, true);
final var elseType = analyzeValueExpression(ifExpr.elseExpression(), context, expectedType).type();
if (!typeOps.compatible(thenType, elseType)) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_IF_BRANCH_TYPE_MISMATCH.name(),
"If expression branches must have compatible types",
ifExpr.span());
return ExprResult.type(TypeView.unknown());
}
return ExprResult.type(thenType);
}
ExprResult analyzeElseExpression(
final PbsAst.ElseExpr elseExpr,
final PbsFlowExpressionContext context) {
final var diagnostics = context.diagnostics();
final var optional = analyzeValueExpression(elseExpr.optionalExpression(), context, null).type();
if (optional.kind() != Kind.OPTIONAL) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_ELSE_NON_OPTIONAL_LEFT.name(),
"Left operand of 'else' must have optional type",
elseExpr.optionalExpression().span());
analyzeValueExpression(elseExpr.fallbackExpression(), context, null);
return ExprResult.type(TypeView.unknown());
}
final var payload = optional.inner();
final var fallback = analyzeValueExpression(elseExpr.fallbackExpression(), context, payload).type();
if (!typeOps.compatible(fallback, payload)) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_ELSE_FALLBACK_TYPE_MISMATCH.name(),
"Fallback expression in 'else' must match optional payload type",
elseExpr.fallbackExpression().span());
}
return ExprResult.type(payload);
}
ExprResult analyzePropagateExpression(
final PbsAst.PropagateExpr propagateExpr,
final PbsFlowExpressionContext context) {
final var diagnostics = context.diagnostics();
final var resultErrorName = context.resultErrorName();
final var source = analyzeValueExpression(propagateExpr.expression(), context, null).type();
if (source.kind() != Kind.RESULT) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_NON_RESULT.name(),
"Propagation operator '!' requires result type",
propagateExpr.expression().span());
return ExprResult.type(TypeView.unknown());
}
final var sourceError = source.errorType();
if (resultErrorName == null || sourceError == null || !resultErrorName.equals(sourceError.name())) {
Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH.name(),
"Propagation operator '!' requires matching result error type in enclosing callable",
propagateExpr.span());
}
return ExprResult.type(source.inner());
}
private ExprResult analyzeValueExpression(
final PbsAst.Expression expression,
final PbsFlowExpressionContext context,
final TypeView expectedType) {
return expressionAnalyzerDelegate.analyze(
expression,
context.withExpectedType(expectedType)
.withUse(ExprUse.VALUE)
.withValueContext(true));
}
}

View File

@ -1,22 +1,13 @@
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.pbs.semantics.PbsFlowSemanticSupport.CallableSymbol;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Model;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Scope;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TupleField;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
final class PbsFlowExpressionAnalyzer {
@FunctionalInterface
interface BlockAnalyzer {
@ -35,6 +26,7 @@ final class PbsFlowExpressionAnalyzer {
private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer;
private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer;
private final PbsFlowControlExpressionAnalyzer controlExpressionAnalyzer;
private final PbsFlowBranchExpressionAnalyzer branchExpressionAnalyzer;
PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) {
this.typeOps = typeOps;
@ -49,6 +41,10 @@ final class PbsFlowExpressionAnalyzer {
typeOps,
this::analyzeExpressionInternal,
this::analyzeBlock);
this.branchExpressionAnalyzer = new PbsFlowBranchExpressionAnalyzer(
typeOps,
this::analyzeExpressionInternal,
this::analyzeBlock);
}
ExprResult analyzeExpression(
@ -81,14 +77,6 @@ final class PbsFlowExpressionAnalyzer {
private ExprResult analyzeExpressionInternal(
final PbsAst.Expression expression,
final PbsFlowExpressionContext context) {
final var scope = context.scope();
final var expectedType = context.expectedType();
final var returnType = context.returnType();
final var resultErrorName = context.resultErrorName();
final var receiverType = context.receiverType();
final var model = context.model();
final var diagnostics = context.diagnostics();
final var use = context.use();
final var structural = structuralExpressionAnalyzer.analyze(expression, context);
if (structural != null) {
return structural;
@ -103,67 +91,16 @@ final class PbsFlowExpressionAnalyzer {
return callableResolutionAnalyzer.analyzeApplyExpression(applyExpr, context);
}
if (expression instanceof PbsAst.IfExpr ifExpr) {
final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type();
if (!typeOps.isBool(condition)) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(),
"If expression condition must have bool type",
ifExpr.condition().span());
}
final var thenType = analyzeBlock(ifExpr.thenBlock(), context, true);
final var elseType = analyzeValueExpression(ifExpr.elseExpression(), context, expectedType).type();
if (!typeOps.compatible(thenType, elseType)) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_IF_BRANCH_TYPE_MISMATCH.name(),
"If expression branches must have compatible types",
ifExpr.span());
return ExprResult.type(TypeView.unknown());
}
return ExprResult.type(thenType);
return branchExpressionAnalyzer.analyzeIfExpression(ifExpr, context);
}
if (expression instanceof PbsAst.SwitchExpr switchExpr) {
return ExprResult.type(controlExpressionAnalyzer.analyzeSwitchExpression(switchExpr, context));
}
if (expression instanceof PbsAst.ElseExpr elseExpr) {
final var optional = analyzeValueExpression(elseExpr.optionalExpression(), context, null).type();
if (optional.kind() != Kind.OPTIONAL) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_ELSE_NON_OPTIONAL_LEFT.name(),
"Left operand of 'else' must have optional type",
elseExpr.optionalExpression().span());
analyzeValueExpression(elseExpr.fallbackExpression(), context, null);
return ExprResult.type(TypeView.unknown());
}
final var payload = optional.inner();
final var fallback = analyzeValueExpression(elseExpr.fallbackExpression(), context, payload).type();
if (!typeOps.compatible(fallback, payload)) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_ELSE_FALLBACK_TYPE_MISMATCH.name(),
"Fallback expression in 'else' must match optional payload type",
elseExpr.fallbackExpression().span());
}
return ExprResult.type(payload);
return branchExpressionAnalyzer.analyzeElseExpression(elseExpr, context);
}
if (expression instanceof PbsAst.PropagateExpr propagateExpr) {
final var source = analyzeValueExpression(propagateExpr.expression(), context, null).type();
if (source.kind() != Kind.RESULT) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_NON_RESULT.name(),
"Propagation operator '!' requires result type",
propagateExpr.expression().span());
return ExprResult.type(TypeView.unknown());
}
final var sourceError = source.errorType();
if (resultErrorName == null || sourceError == null || !resultErrorName.equals(sourceError.name())) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH.name(),
"Propagation operator '!' requires matching result error type in enclosing callable",
propagateExpr.span());
}
return ExprResult.type(source.inner());
return branchExpressionAnalyzer.analyzePropagateExpression(propagateExpr, context);
}
if (expression instanceof PbsAst.HandleExpr handleExpr) {
return ExprResult.type(controlExpressionAnalyzer.analyzeHandleExpression(handleExpr, context));
@ -175,28 +112,6 @@ final class PbsFlowExpressionAnalyzer {
return ExprResult.type(TypeView.unknown());
}
private ExprResult analyzeValueExpression(
final PbsAst.Expression expression,
final PbsFlowExpressionContext context,
final TypeView expectedType) {
return analyzeExpressionInternal(
expression,
context.withExpectedType(expectedType)
.withUse(ExprUse.VALUE)
.withValueContext(true));
}
private ExprResult analyzeTargetExpression(
final PbsAst.Expression expression,
final PbsFlowExpressionContext context,
final ExprUse use) {
return analyzeExpressionInternal(
expression,
context.withExpectedType(null)
.withUse(use)
.withValueContext(true));
}
private TypeView analyzeBlock(
final PbsAst.Block block,
final PbsFlowExpressionContext context,