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;
|
||||
|
||||
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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user