implements PR-12.5
This commit is contained in:
parent
dd598b3989
commit
df0d9f4601
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,22 +1,13 @@
|
|||||||
package p.studio.compiler.pbs.semantics;
|
package p.studio.compiler.pbs.semantics;
|
||||||
|
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
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.DiagnosticSink;
|
||||||
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.CallableSymbol;
|
|
||||||
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult;
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult;
|
||||||
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse;
|
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.Model;
|
||||||
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Scope;
|
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 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 {
|
final class PbsFlowExpressionAnalyzer {
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface BlockAnalyzer {
|
interface BlockAnalyzer {
|
||||||
@ -35,6 +26,7 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer;
|
private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer;
|
||||||
private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer;
|
private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer;
|
||||||
private final PbsFlowControlExpressionAnalyzer controlExpressionAnalyzer;
|
private final PbsFlowControlExpressionAnalyzer controlExpressionAnalyzer;
|
||||||
|
private final PbsFlowBranchExpressionAnalyzer branchExpressionAnalyzer;
|
||||||
|
|
||||||
PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) {
|
PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) {
|
||||||
this.typeOps = typeOps;
|
this.typeOps = typeOps;
|
||||||
@ -49,6 +41,10 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
typeOps,
|
typeOps,
|
||||||
this::analyzeExpressionInternal,
|
this::analyzeExpressionInternal,
|
||||||
this::analyzeBlock);
|
this::analyzeBlock);
|
||||||
|
this.branchExpressionAnalyzer = new PbsFlowBranchExpressionAnalyzer(
|
||||||
|
typeOps,
|
||||||
|
this::analyzeExpressionInternal,
|
||||||
|
this::analyzeBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprResult analyzeExpression(
|
ExprResult analyzeExpression(
|
||||||
@ -81,14 +77,6 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
private ExprResult analyzeExpressionInternal(
|
private ExprResult analyzeExpressionInternal(
|
||||||
final PbsAst.Expression expression,
|
final PbsAst.Expression expression,
|
||||||
final PbsFlowExpressionContext context) {
|
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);
|
final var structural = structuralExpressionAnalyzer.analyze(expression, context);
|
||||||
if (structural != null) {
|
if (structural != null) {
|
||||||
return structural;
|
return structural;
|
||||||
@ -103,67 +91,16 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
return callableResolutionAnalyzer.analyzeApplyExpression(applyExpr, context);
|
return callableResolutionAnalyzer.analyzeApplyExpression(applyExpr, context);
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.IfExpr ifExpr) {
|
if (expression instanceof PbsAst.IfExpr ifExpr) {
|
||||||
final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type();
|
return branchExpressionAnalyzer.analyzeIfExpression(ifExpr, context);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.SwitchExpr switchExpr) {
|
if (expression instanceof PbsAst.SwitchExpr switchExpr) {
|
||||||
return ExprResult.type(controlExpressionAnalyzer.analyzeSwitchExpression(switchExpr, context));
|
return ExprResult.type(controlExpressionAnalyzer.analyzeSwitchExpression(switchExpr, context));
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.ElseExpr elseExpr) {
|
if (expression instanceof PbsAst.ElseExpr elseExpr) {
|
||||||
final var optional = analyzeValueExpression(elseExpr.optionalExpression(), context, null).type();
|
return branchExpressionAnalyzer.analyzeElseExpression(elseExpr, context);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.PropagateExpr propagateExpr) {
|
if (expression instanceof PbsAst.PropagateExpr propagateExpr) {
|
||||||
final var source = analyzeValueExpression(propagateExpr.expression(), context, null).type();
|
return branchExpressionAnalyzer.analyzePropagateExpression(propagateExpr, context);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.HandleExpr handleExpr) {
|
if (expression instanceof PbsAst.HandleExpr handleExpr) {
|
||||||
return ExprResult.type(controlExpressionAnalyzer.analyzeHandleExpression(handleExpr, context));
|
return ExprResult.type(controlExpressionAnalyzer.analyzeHandleExpression(handleExpr, context));
|
||||||
@ -175,28 +112,6 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
return ExprResult.type(TypeView.unknown());
|
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(
|
private TypeView analyzeBlock(
|
||||||
final PbsAst.Block block,
|
final PbsAst.Block block,
|
||||||
final PbsFlowExpressionContext context,
|
final PbsFlowExpressionContext context,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user