implements PR-13.1

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

View File

@ -33,11 +33,16 @@ final class PbsFlowBodyAnalyzer {
validateCallableBody( validateCallableBody(
functionDecl.parameters(), functionDecl.parameters(),
functionDecl.body(), functionDecl.body(),
typeOps.callableReturnType(functionDecl.returnKind(), functionDecl.returnType(), functionDecl.resultErrorType(), model), newCallableContext(
typeOps.callableReturnType(
functionDecl.returnKind(),
functionDecl.returnType(),
functionDecl.resultErrorType(),
model),
functionDecl.resultErrorType() == null ? null : functionDecl.resultErrorType().name(), functionDecl.resultErrorType() == null ? null : functionDecl.resultErrorType().name(),
null, null,
model, model,
diagnostics); diagnostics));
continue; continue;
} }
if (topDecl instanceof PbsAst.StructDecl structDecl) { if (topDecl instanceof PbsAst.StructDecl structDecl) {
@ -46,21 +51,27 @@ final class PbsFlowBodyAnalyzer {
validateCallableBody( validateCallableBody(
method.parameters(), method.parameters(),
method.body(), method.body(),
typeOps.callableReturnType(method.returnKind(), method.returnType(), method.resultErrorType(), model), newCallableContext(
typeOps.callableReturnType(
method.returnKind(),
method.returnType(),
method.resultErrorType(),
model),
method.resultErrorType() == null ? null : method.resultErrorType().name(), method.resultErrorType() == null ? null : method.resultErrorType().name(),
receiverType, receiverType,
model, model,
diagnostics); diagnostics));
} }
for (final var ctor : structDecl.ctors()) { for (final var ctor : structDecl.ctors()) {
validateCallableBody( validateCallableBody(
ctor.parameters(), ctor.parameters(),
ctor.body(), ctor.body(),
newCallableContext(
TypeView.unit(), TypeView.unit(),
null, null,
receiverType, receiverType,
model, model,
diagnostics); diagnostics));
} }
continue; continue;
} }
@ -70,37 +81,57 @@ final class PbsFlowBodyAnalyzer {
validateCallableBody( validateCallableBody(
method.parameters(), method.parameters(),
method.body(), method.body(),
typeOps.callableReturnType(method.returnKind(), method.returnType(), method.resultErrorType(), model), newCallableContext(
typeOps.callableReturnType(
method.returnKind(),
method.returnType(),
method.resultErrorType(),
model),
method.resultErrorType() == null ? null : method.resultErrorType().name(), method.resultErrorType() == null ? null : method.resultErrorType().name(),
receiverType, receiverType,
model, model,
diagnostics); diagnostics));
} }
} }
} }
} }
private void validateCallableBody( private PbsFlowBodyContext newCallableContext(
final ReadOnlyList<PbsAst.Parameter> parameters,
final PbsAst.Block body,
final TypeView returnType, final TypeView returnType,
final String resultErrorName, final String resultErrorName,
final TypeView receiverType, final TypeView receiverType,
final Model model, final Model model,
final DiagnosticSink diagnostics) { final DiagnosticSink diagnostics) {
final var scope = new Scope(); return new PbsFlowBodyContext(
for (final var parameter : parameters) { new Scope(),
scope.bind(parameter.name(), typeOps.typeFromTypeRef(parameter.typeRef(), model, receiverType)); returnType,
resultErrorName,
receiverType,
model,
diagnostics);
} }
analyzeBlock(body, scope, returnType, resultErrorName, receiverType, model, diagnostics, true);
validateCallableCompletion(body, returnType, model, diagnostics); private void validateCallableBody(
final ReadOnlyList<PbsAst.Parameter> parameters,
final PbsAst.Block body,
final PbsFlowBodyContext context) {
final var scope = context.scope();
for (final var parameter : parameters) {
scope.bind(
parameter.name(),
typeOps.typeFromTypeRef(parameter.typeRef(), context.model(), context.receiverType()));
}
final var callableContext = context.withScope(scope);
analyzeBlock(body, callableContext, true);
validateCallableCompletion(body, callableContext);
} }
private void validateCallableCompletion( private void validateCallableCompletion(
final PbsAst.Block body, final PbsAst.Block body,
final TypeView returnType, final PbsFlowBodyContext context) {
final Model model, final var returnType = context.returnType();
final DiagnosticSink diagnostics) { final var model = context.model();
final var diagnostics = context.diagnostics();
final var returnKind = returnType.kind(); final var returnKind = returnType.kind();
final var enforceResultCompletion = returnKind == Kind.RESULT; final var enforceResultCompletion = returnKind == Kind.RESULT;
final var enforcePlainNonUnitCompletion = returnKind != Kind.RESULT final var enforcePlainNonUnitCompletion = returnKind != Kind.RESULT
@ -285,9 +316,26 @@ final class PbsFlowBodyAnalyzer {
final Model model, final Model model,
final DiagnosticSink diagnostics, final DiagnosticSink diagnostics,
final boolean valueContext) { final boolean valueContext) {
final var scope = outerScope.copy(); return analyzeBlock(
block,
new PbsFlowBodyContext(
outerScope,
returnType,
resultErrorName,
receiverType,
model,
diagnostics),
valueContext);
}
private TypeView analyzeBlock(
final PbsAst.Block block,
final PbsFlowBodyContext context,
final boolean valueContext) {
final var scope = context.scope().copy();
final var scopedContext = context.withScope(scope);
for (final var statement : block.statements()) { for (final var statement : block.statements()) {
analyzeStatement(statement, scope, returnType, resultErrorName, receiverType, model, diagnostics); analyzeStatement(statement, scopedContext);
} }
if (block.tailExpression() == null) { if (block.tailExpression() == null) {
return TypeView.unit(); return TypeView.unit();
@ -296,11 +344,11 @@ final class PbsFlowBodyAnalyzer {
block.tailExpression(), block.tailExpression(),
scope, scope,
null, null,
returnType, context.returnType(),
resultErrorName, context.resultErrorName(),
receiverType, context.receiverType(),
model, context.model(),
diagnostics, context.diagnostics(),
ExprUse.VALUE, ExprUse.VALUE,
valueContext, valueContext,
this::analyzeBlock).type(); this::analyzeBlock).type();
@ -308,12 +356,13 @@ final class PbsFlowBodyAnalyzer {
private void analyzeStatement( private void analyzeStatement(
final PbsAst.Statement statement, final PbsAst.Statement statement,
final Scope scope, final PbsFlowBodyContext context) {
final TypeView returnType, final var scope = context.scope();
final String resultErrorName, final var returnType = context.returnType();
final TypeView receiverType, final var resultErrorName = context.resultErrorName();
final Model model, final var receiverType = context.receiverType();
final DiagnosticSink diagnostics) { final var model = context.model();
final var diagnostics = context.diagnostics();
if (statement instanceof PbsAst.LetStatement letStatement) { if (statement instanceof PbsAst.LetStatement letStatement) {
final var expected = letStatement.explicitType() == null final var expected = letStatement.explicitType() == null
? null ? null
@ -335,14 +384,7 @@ final class PbsFlowBodyAnalyzer {
} }
if (statement instanceof PbsAst.ReturnStatement returnStatement) { if (statement instanceof PbsAst.ReturnStatement returnStatement) {
if (returnStatement.value() != null) { if (returnStatement.value() != null) {
analyzeReturnStatement( analyzeReturnStatement(returnStatement.value(), context);
returnStatement.value(),
scope,
returnType,
resultErrorName,
receiverType,
model,
diagnostics);
} }
return; return;
} }
@ -365,12 +407,12 @@ final class PbsFlowBodyAnalyzer {
"If statement condition must have bool type", "If statement condition must have bool type",
ifStatement.condition().span()); ifStatement.condition().span());
} }
analyzeBlock(ifStatement.thenBlock(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false); analyzeBlock(ifStatement.thenBlock(), context, false);
if (ifStatement.elseBlock() != null) { if (ifStatement.elseBlock() != null) {
analyzeBlock(ifStatement.elseBlock(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false); analyzeBlock(ifStatement.elseBlock(), context, false);
} }
if (ifStatement.elseIf() != null) { if (ifStatement.elseIf() != null) {
analyzeStatement(ifStatement.elseIf(), scope, returnType, resultErrorName, receiverType, model, diagnostics); analyzeStatement(ifStatement.elseIf(), context);
} }
return; return;
} }
@ -432,7 +474,7 @@ final class PbsFlowBodyAnalyzer {
} }
final var bodyScope = scope.copy(); final var bodyScope = scope.copy();
bodyScope.bind(iteratorName, iteratorType); bodyScope.bind(iteratorName, iteratorType);
analyzeBlock(body, bodyScope, returnType, resultErrorName, receiverType, model, diagnostics, false); analyzeBlock(body, context.withScope(bodyScope), false);
return; return;
} }
if (statement instanceof PbsAst.WhileStatement whileStatement) { if (statement instanceof PbsAst.WhileStatement whileStatement) {
@ -454,7 +496,7 @@ final class PbsFlowBodyAnalyzer {
"While condition must have bool type", "While condition must have bool type",
whileStatement.condition().span()); whileStatement.condition().span());
} }
analyzeBlock(whileStatement.body(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false); analyzeBlock(whileStatement.body(), context, false);
return; return;
} }
if (statement instanceof PbsAst.ExpressionStatement expressionStatement) { if (statement instanceof PbsAst.ExpressionStatement expressionStatement) {
@ -473,36 +515,31 @@ final class PbsFlowBodyAnalyzer {
return; return;
} }
if (statement instanceof PbsAst.AssignStatement assignStatement) { if (statement instanceof PbsAst.AssignStatement assignStatement) {
analyzeAssignmentStatement(assignStatement, scope, returnType, resultErrorName, receiverType, model, diagnostics); analyzeAssignmentStatement(assignStatement, context);
} }
} }
private void analyzeReturnStatement( private void analyzeReturnStatement(
final PbsAst.Expression value, final PbsAst.Expression value,
final Scope scope, final PbsFlowBodyContext context) {
final TypeView returnType,
final String resultErrorName,
final TypeView receiverType,
final Model model,
final DiagnosticSink diagnostics) {
final var root = unwrapGroup(value); final var root = unwrapGroup(value);
if (root instanceof PbsAst.OkExpr okExpr) { if (root instanceof PbsAst.OkExpr okExpr) {
analyzeReturnOk(okExpr, scope, returnType, resultErrorName, receiverType, model, diagnostics); analyzeReturnOk(okExpr, context);
return; return;
} }
if (root instanceof PbsAst.ErrExpr errExpr) { if (root instanceof PbsAst.ErrExpr errExpr) {
analyzeReturnErr(errExpr, returnType, resultErrorName, model, diagnostics); analyzeReturnErr(errExpr, context);
return; return;
} }
expressionAnalyzer.analyzeExpression( expressionAnalyzer.analyzeExpression(
value, value,
scope, context.scope(),
returnType, context.returnType(),
returnType, context.returnType(),
resultErrorName, context.resultErrorName(),
receiverType, context.receiverType(),
model, context.model(),
diagnostics, context.diagnostics(),
ExprUse.VALUE, ExprUse.VALUE,
true, true,
this::analyzeBlock); this::analyzeBlock);
@ -510,12 +547,10 @@ final class PbsFlowBodyAnalyzer {
private void analyzeReturnOk( private void analyzeReturnOk(
final PbsAst.OkExpr okExpr, final PbsAst.OkExpr okExpr,
final Scope scope, final PbsFlowBodyContext context) {
final TypeView returnType, final var returnType = context.returnType();
final String resultErrorName, final var resultErrorName = context.resultErrorName();
final TypeView receiverType, final var diagnostics = context.diagnostics();
final Model model,
final DiagnosticSink diagnostics) {
if (returnType.kind() != Kind.RESULT || resultErrorName == null) { if (returnType.kind() != Kind.RESULT || resultErrorName == null) {
Diagnostics.error(diagnostics, Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(), PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(),
@ -523,12 +558,12 @@ final class PbsFlowBodyAnalyzer {
okExpr.span()); okExpr.span());
expressionAnalyzer.analyzeExpression( expressionAnalyzer.analyzeExpression(
okExpr.value(), okExpr.value(),
scope, context.scope(),
null, null,
returnType, returnType,
resultErrorName, resultErrorName,
receiverType, context.receiverType(),
model, context.model(),
diagnostics, diagnostics,
ExprUse.VALUE, ExprUse.VALUE,
true, true,
@ -539,12 +574,12 @@ final class PbsFlowBodyAnalyzer {
final var payloadType = returnType.inner(); final var payloadType = returnType.inner();
final var actualType = expressionAnalyzer.analyzeExpression( final var actualType = expressionAnalyzer.analyzeExpression(
okExpr.value(), okExpr.value(),
scope, context.scope(),
payloadType, payloadType,
returnType, returnType,
resultErrorName, resultErrorName,
receiverType, context.receiverType(),
model, context.model(),
diagnostics, diagnostics,
ExprUse.VALUE, ExprUse.VALUE,
true, true,
@ -559,10 +594,11 @@ final class PbsFlowBodyAnalyzer {
private void analyzeReturnErr( private void analyzeReturnErr(
final PbsAst.ErrExpr errExpr, final PbsAst.ErrExpr errExpr,
final TypeView returnType, final PbsFlowBodyContext context) {
final String resultErrorName, final var returnType = context.returnType();
final Model model, final var resultErrorName = context.resultErrorName();
final DiagnosticSink diagnostics) { final var model = context.model();
final var diagnostics = context.diagnostics();
if (returnType.kind() != Kind.RESULT || resultErrorName == null) { if (returnType.kind() != Kind.RESULT || resultErrorName == null) {
Diagnostics.error(diagnostics, Diagnostics.error(diagnostics,
PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(), PbsSemanticsErrors.E_SEM_RESULT_FLOW_INVALID_POSITION.name(),
@ -601,13 +637,14 @@ final class PbsFlowBodyAnalyzer {
private void analyzeAssignmentStatement( private void analyzeAssignmentStatement(
final PbsAst.AssignStatement assignStatement, final PbsAst.AssignStatement assignStatement,
final Scope scope, final PbsFlowBodyContext context) {
final TypeView returnType, final var scope = context.scope();
final String resultErrorName, final var returnType = context.returnType();
final TypeView receiverType, final var resultErrorName = context.resultErrorName();
final Model model, final var receiverType = context.receiverType();
final DiagnosticSink diagnostics) { final var model = context.model();
final var target = resolveAssignmentTarget(assignStatement.target(), scope, receiverType, model, diagnostics); final var diagnostics = context.diagnostics();
final var target = resolveAssignmentTarget(assignStatement.target(), context);
final var expectedType = target.type(); final var expectedType = target.type();
final var valueType = expressionAnalyzer.analyzeExpression( final var valueType = expressionAnalyzer.analyzeExpression(
assignStatement.value(), assignStatement.value(),
@ -636,10 +673,11 @@ final class PbsFlowBodyAnalyzer {
private AssignmentTargetResolution resolveAssignmentTarget( private AssignmentTargetResolution resolveAssignmentTarget(
final PbsAst.LValue lValue, final PbsAst.LValue lValue,
final Scope scope, final PbsFlowBodyContext context) {
final TypeView receiverType, final var scope = context.scope();
final Model model, final var receiverType = context.receiverType();
final DiagnosticSink diagnostics) { final var model = context.model();
final var diagnostics = context.diagnostics();
final var rootName = lValue.rootName(); final var rootName = lValue.rootName();
final var rootIsThis = "this".equals(rootName); final var rootIsThis = "this".equals(rootName);
if (lValue.pathSegments().isEmpty()) { if (lValue.pathSegments().isEmpty()) {

View File

@ -0,0 +1,64 @@
package p.studio.compiler.pbs.semantics;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Model;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Scope;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
final class PbsFlowBodyContext {
private final Scope scope;
private final TypeView returnType;
private final String resultErrorName;
private final TypeView receiverType;
private final Model model;
private final DiagnosticSink diagnostics;
PbsFlowBodyContext(
final Scope scope,
final TypeView returnType,
final String resultErrorName,
final TypeView receiverType,
final Model model,
final DiagnosticSink diagnostics) {
this.scope = scope;
this.returnType = returnType;
this.resultErrorName = resultErrorName;
this.receiverType = receiverType;
this.model = model;
this.diagnostics = diagnostics;
}
Scope scope() {
return scope;
}
TypeView returnType() {
return returnType;
}
String resultErrorName() {
return resultErrorName;
}
TypeView receiverType() {
return receiverType;
}
Model model() {
return model;
}
DiagnosticSink diagnostics() {
return diagnostics;
}
PbsFlowBodyContext withScope(final Scope nextScope) {
return new PbsFlowBodyContext(
nextScope,
returnType,
resultErrorName,
receiverType,
model,
diagnostics);
}
}