implements PR-12.3
This commit is contained in:
parent
8692e94a27
commit
0652c1ab28
@ -0,0 +1,349 @@
|
|||||||
|
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.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.TupleField;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
final class PbsFlowCallableResolutionAnalyzer {
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ExpressionAnalyzerDelegate {
|
||||||
|
ExprResult analyze(PbsAst.Expression expression, PbsFlowExpressionContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PbsFlowTypeOps typeOps;
|
||||||
|
private final ExpressionAnalyzerDelegate expressionAnalyzerDelegate;
|
||||||
|
|
||||||
|
PbsFlowCallableResolutionAnalyzer(
|
||||||
|
final PbsFlowTypeOps typeOps,
|
||||||
|
final ExpressionAnalyzerDelegate expressionAnalyzerDelegate) {
|
||||||
|
this.typeOps = typeOps;
|
||||||
|
this.expressionAnalyzerDelegate = expressionAnalyzerDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprResult analyzeCallExpression(
|
||||||
|
final PbsAst.CallExpr callExpr,
|
||||||
|
final PbsFlowExpressionContext context) {
|
||||||
|
final var callee = analyzeTargetExpression(callExpr.callee(), context, ExprUse.CALL_TARGET);
|
||||||
|
|
||||||
|
final TypeView argumentType;
|
||||||
|
if (callExpr.arguments().isEmpty()) {
|
||||||
|
argumentType = TypeView.unit();
|
||||||
|
} else if (callExpr.arguments().size() == 1) {
|
||||||
|
argumentType = analyzeValueExpression(callExpr.arguments().getFirst(), context, null).type();
|
||||||
|
} else {
|
||||||
|
final var fields = new ArrayList<TupleField>(callExpr.arguments().size());
|
||||||
|
for (final var argument : callExpr.arguments()) {
|
||||||
|
final var itemType = analyzeValueExpression(argument, context, null).type();
|
||||||
|
fields.add(new TupleField(null, itemType));
|
||||||
|
}
|
||||||
|
argumentType = TypeView.tuple(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveCallableApplication(
|
||||||
|
callExpr.span(),
|
||||||
|
callExpr.callee().span(),
|
||||||
|
callee,
|
||||||
|
argumentType,
|
||||||
|
context.expectedType(),
|
||||||
|
context.diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprResult analyzeApplyExpression(
|
||||||
|
final PbsAst.ApplyExpr applyExpr,
|
||||||
|
final PbsFlowExpressionContext context) {
|
||||||
|
final var callee = analyzeTargetExpression(applyExpr.callee(), context, ExprUse.APPLY_TARGET);
|
||||||
|
final var argument = analyzeValueExpression(applyExpr.argument(), context, null).type();
|
||||||
|
|
||||||
|
return resolveCallableApplication(
|
||||||
|
applyExpr.span(),
|
||||||
|
applyExpr.callee().span(),
|
||||||
|
callee,
|
||||||
|
argument,
|
||||||
|
context.expectedType(),
|
||||||
|
context.diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprResult analyzeMemberExpression(
|
||||||
|
final PbsAst.MemberExpr memberExpr,
|
||||||
|
final PbsFlowExpressionContext context) {
|
||||||
|
final var receiver = analyzeValueExpression(memberExpr.receiver(), context, null).type();
|
||||||
|
final var model = context.model();
|
||||||
|
final var diagnostics = context.diagnostics();
|
||||||
|
final var receiverType = context.receiverType();
|
||||||
|
final var use = context.use();
|
||||||
|
|
||||||
|
if (receiver.kind() == Kind.TYPE_REF) {
|
||||||
|
final var enumCases = model.enums.get(receiver.name());
|
||||||
|
if (enumCases != null && enumCases.contains(memberExpr.memberName())) {
|
||||||
|
return ExprResult.type(TypeView.enumType(receiver.name()));
|
||||||
|
}
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Invalid type member access",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver.kind() == Kind.TUPLE) {
|
||||||
|
if (receiver.tupleFields().size() <= 1) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Member projection is not defined for single-slot carrier values",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
for (final var field : receiver.tupleFields()) {
|
||||||
|
if (memberExpr.memberName().equals(field.label())) {
|
||||||
|
return ExprResult.type(field.type());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Tuple label '%s' does not exist".formatted(memberExpr.memberName()),
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver.kind() == Kind.STRUCT) {
|
||||||
|
final var struct = model.structs.get(receiver.name());
|
||||||
|
if (struct == null) {
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
final var field = struct.fields().get(memberExpr.memberName());
|
||||||
|
if (field != null) {
|
||||||
|
if (!canReadStructField(field, receiver, memberExpr.receiver(), receiverType)) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_FIELD_READ_ACCESS_DENIED.name(),
|
||||||
|
"Read access to struct field '%s' is not permitted".formatted(memberExpr.memberName()),
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
return ExprResult.type(field.type());
|
||||||
|
}
|
||||||
|
final var methods = struct.methods().get(memberExpr.memberName());
|
||||||
|
if (methods != null && !methods.isEmpty()) {
|
||||||
|
if (use == ExprUse.VALUE) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
||||||
|
"Bare method extraction is not allowed in PBS core",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
return ExprResult.callables(methods, true);
|
||||||
|
}
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Struct member '%s' does not exist".formatted(memberExpr.memberName()),
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver.kind() == Kind.SERVICE) {
|
||||||
|
final var service = model.services.get(receiver.name());
|
||||||
|
if (service == null) {
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
final var methods = service.methods().get(memberExpr.memberName());
|
||||||
|
if (methods != null && !methods.isEmpty()) {
|
||||||
|
if (use == ExprUse.VALUE) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
||||||
|
"Bare method extraction is not allowed in PBS core",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
return ExprResult.callables(methods, true);
|
||||||
|
}
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Service member '%s' does not exist".formatted(memberExpr.memberName()),
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver.kind() == Kind.CONTRACT) {
|
||||||
|
final var contract = model.contracts.get(receiver.name());
|
||||||
|
if (contract == null) {
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
final var methods = contract.methods().get(memberExpr.memberName());
|
||||||
|
if (methods != null && !methods.isEmpty()) {
|
||||||
|
if (use == ExprUse.VALUE) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
||||||
|
"Bare method extraction is not allowed in PBS core",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
return ExprResult.callables(methods, true);
|
||||||
|
}
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Contract member '%s' does not exist".formatted(memberExpr.memberName()),
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
||||||
|
"Invalid member access target",
|
||||||
|
memberExpr.span());
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprResult analyzeBindExpression(
|
||||||
|
final PbsAst.BindExpr bindExpr,
|
||||||
|
final PbsFlowExpressionContext context) {
|
||||||
|
final var contextType = analyzeValueExpression(bindExpr.contextExpression(), context, null).type();
|
||||||
|
final var expectedType = context.expectedType();
|
||||||
|
final var model = context.model();
|
||||||
|
final var diagnostics = context.diagnostics();
|
||||||
|
if (expectedType == null || expectedType.kind() != Kind.CALLBACK) {
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
final var topLevel = model.topLevelCallables.get(bindExpr.functionName());
|
||||||
|
if (topLevel == null || topLevel.isEmpty()) {
|
||||||
|
return ExprResult.type(expectedType);
|
||||||
|
}
|
||||||
|
final var compatible = new ArrayList<CallableSymbol>();
|
||||||
|
for (final var candidate : topLevel) {
|
||||||
|
if (candidate.inputTypes().size() != expectedType.callbackInputs().size() + 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!typeOps.compatible(contextType, candidate.inputTypes().getFirst())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var ok = true;
|
||||||
|
for (int i = 0; i < expectedType.callbackInputs().size(); i++) {
|
||||||
|
if (!typeOps.compatible(expectedType.callbackInputs().get(i), candidate.inputTypes().get(i + 1))) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!typeOps.compatible(candidate.outputType(), expectedType.callbackOutput())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
compatible.add(candidate);
|
||||||
|
}
|
||||||
|
if (compatible.size() > 1) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
||||||
|
"Bind target resolves ambiguously for callback type",
|
||||||
|
bindExpr.span());
|
||||||
|
} else if (compatible.isEmpty()) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_UNRESOLVED_OVERLOAD.name(),
|
||||||
|
"Bind target does not resolve for callback type",
|
||||||
|
bindExpr.span());
|
||||||
|
}
|
||||||
|
return ExprResult.type(expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExprResult resolveCallableApplication(
|
||||||
|
final Span wholeSpan,
|
||||||
|
final Span calleeSpan,
|
||||||
|
final ExprResult callee,
|
||||||
|
final TypeView argumentType,
|
||||||
|
final TypeView expectedType,
|
||||||
|
final DiagnosticSink diagnostics) {
|
||||||
|
final var candidates = callee.callables();
|
||||||
|
if (candidates.isEmpty()) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_NON_CALLABLE_TARGET.name(),
|
||||||
|
"Apply/call target is not callable",
|
||||||
|
calleeSpan);
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
final var compatible = new ArrayList<CallableSymbol>();
|
||||||
|
for (final var candidate : candidates) {
|
||||||
|
if (typeOps.inputCompatible(candidate.inputTypes(), argumentType)) {
|
||||||
|
compatible.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatible.isEmpty()) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_UNRESOLVED_OVERLOAD.name(),
|
||||||
|
"No callable overload matches the provided argument",
|
||||||
|
wholeSpan);
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatible.size() > 1 && expectedType != null) {
|
||||||
|
final var narrowed = compatible.stream()
|
||||||
|
.filter(candidate -> typeOps.compatible(candidate.outputType(), expectedType))
|
||||||
|
.toList();
|
||||||
|
if (narrowed.size() == 1) {
|
||||||
|
return ExprResult.type(narrowed.getFirst().outputType());
|
||||||
|
}
|
||||||
|
if (!narrowed.isEmpty()) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
||||||
|
"Callable overload resolution remains ambiguous",
|
||||||
|
wholeSpan);
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatible.size() > 1) {
|
||||||
|
Diagnostics.error(diagnostics,
|
||||||
|
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
||||||
|
"Callable overload resolution is ambiguous",
|
||||||
|
wholeSpan);
|
||||||
|
return ExprResult.type(TypeView.unknown());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExprResult.type(compatible.getFirst().outputType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canReadStructField(
|
||||||
|
final PbsFlowSemanticSupport.StructFieldInfo field,
|
||||||
|
final TypeView receiver,
|
||||||
|
final PbsAst.Expression receiverExpr,
|
||||||
|
final TypeView currentReceiverType) {
|
||||||
|
if (field.isPublic()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return receiverExpr instanceof PbsAst.ThisExpr
|
||||||
|
&& currentReceiverType != null
|
||||||
|
&& currentReceiverType.kind() == Kind.STRUCT
|
||||||
|
&& receiver.kind() == Kind.STRUCT
|
||||||
|
&& receiver.name().equals(currentReceiverType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExprResult analyzeTargetExpression(
|
||||||
|
final PbsAst.Expression expression,
|
||||||
|
final PbsFlowExpressionContext context,
|
||||||
|
final ExprUse use) {
|
||||||
|
return expressionAnalyzerDelegate.analyze(
|
||||||
|
expression,
|
||||||
|
context.withExpectedType(null)
|
||||||
|
.withUse(use)
|
||||||
|
.withValueContext(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,6 +33,7 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
|
|
||||||
private final PbsFlowTypeOps typeOps;
|
private final PbsFlowTypeOps typeOps;
|
||||||
private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer;
|
private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer;
|
||||||
|
private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer;
|
||||||
|
|
||||||
PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) {
|
PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) {
|
||||||
this.typeOps = typeOps;
|
this.typeOps = typeOps;
|
||||||
@ -40,6 +41,9 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
typeOps,
|
typeOps,
|
||||||
this::analyzeExpressionInternal,
|
this::analyzeExpressionInternal,
|
||||||
this::analyzeBlock);
|
this::analyzeBlock);
|
||||||
|
this.callableResolutionAnalyzer = new PbsFlowCallableResolutionAnalyzer(
|
||||||
|
typeOps,
|
||||||
|
this::analyzeExpressionInternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprResult analyzeExpression(
|
ExprResult analyzeExpression(
|
||||||
@ -85,15 +89,13 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
return structural;
|
return structural;
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.MemberExpr memberExpr) {
|
if (expression instanceof PbsAst.MemberExpr memberExpr) {
|
||||||
return analyzeMemberExpression(
|
return callableResolutionAnalyzer.analyzeMemberExpression(memberExpr, context);
|
||||||
memberExpr,
|
|
||||||
context);
|
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.CallExpr callExpr) {
|
if (expression instanceof PbsAst.CallExpr callExpr) {
|
||||||
return analyzeCallExpression(callExpr, context);
|
return callableResolutionAnalyzer.analyzeCallExpression(callExpr, context);
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.ApplyExpr applyExpr) {
|
if (expression instanceof PbsAst.ApplyExpr applyExpr) {
|
||||||
return 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();
|
final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type();
|
||||||
@ -162,299 +164,12 @@ final class PbsFlowExpressionAnalyzer {
|
|||||||
return ExprResult.type(analyzeHandleExpression(handleExpr, context));
|
return ExprResult.type(analyzeHandleExpression(handleExpr, context));
|
||||||
}
|
}
|
||||||
if (expression instanceof PbsAst.BindExpr bindExpr) {
|
if (expression instanceof PbsAst.BindExpr bindExpr) {
|
||||||
final var contextType = analyzeValueExpression(bindExpr.contextExpression(), context, null).type();
|
return callableResolutionAnalyzer.analyzeBindExpression(bindExpr, context);
|
||||||
if (expectedType == null || expectedType.kind() != Kind.CALLBACK) {
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
final var topLevel = model.topLevelCallables.get(bindExpr.functionName());
|
|
||||||
if (topLevel == null || topLevel.isEmpty()) {
|
|
||||||
return ExprResult.type(expectedType);
|
|
||||||
}
|
|
||||||
final var compatible = new ArrayList<CallableSymbol>();
|
|
||||||
for (final var candidate : topLevel) {
|
|
||||||
if (candidate.inputTypes().size() != expectedType.callbackInputs().size() + 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!typeOps.compatible(contextType, candidate.inputTypes().getFirst())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var ok = true;
|
|
||||||
for (int i = 0; i < expectedType.callbackInputs().size(); i++) {
|
|
||||||
if (!typeOps.compatible(expectedType.callbackInputs().get(i), candidate.inputTypes().get(i + 1))) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ok) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!typeOps.compatible(candidate.outputType(), expectedType.callbackOutput())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
compatible.add(candidate);
|
|
||||||
}
|
|
||||||
if (compatible.size() > 1) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
|
||||||
"Bind target resolves ambiguously for callback type",
|
|
||||||
bindExpr.span());
|
|
||||||
} else if (compatible.isEmpty()) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_UNRESOLVED_OVERLOAD.name(),
|
|
||||||
"Bind target does not resolve for callback type",
|
|
||||||
bindExpr.span());
|
|
||||||
}
|
|
||||||
return ExprResult.type(expectedType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
return ExprResult.type(TypeView.unknown());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExprResult analyzeCallExpression(
|
|
||||||
final PbsAst.CallExpr callExpr,
|
|
||||||
final PbsFlowExpressionContext context) {
|
|
||||||
final var callee = analyzeTargetExpression(callExpr.callee(), context, ExprUse.CALL_TARGET);
|
|
||||||
|
|
||||||
final TypeView argumentType;
|
|
||||||
if (callExpr.arguments().isEmpty()) {
|
|
||||||
argumentType = TypeView.unit();
|
|
||||||
} else if (callExpr.arguments().size() == 1) {
|
|
||||||
argumentType = analyzeValueExpression(callExpr.arguments().getFirst(), context, null).type();
|
|
||||||
} else {
|
|
||||||
final var fields = new ArrayList<TupleField>(callExpr.arguments().size());
|
|
||||||
for (final var argument : callExpr.arguments()) {
|
|
||||||
final var itemType = analyzeValueExpression(argument, context, null).type();
|
|
||||||
fields.add(new TupleField(null, itemType));
|
|
||||||
}
|
|
||||||
argumentType = TypeView.tuple(fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolveCallableApplication(
|
|
||||||
callExpr.span(),
|
|
||||||
callExpr.callee().span(),
|
|
||||||
callee,
|
|
||||||
argumentType,
|
|
||||||
context.expectedType(),
|
|
||||||
context.diagnostics());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExprResult analyzeApplyExpression(
|
|
||||||
final PbsAst.ApplyExpr applyExpr,
|
|
||||||
final PbsFlowExpressionContext context) {
|
|
||||||
final var callee = analyzeTargetExpression(applyExpr.callee(), context, ExprUse.APPLY_TARGET);
|
|
||||||
final var argument = analyzeValueExpression(applyExpr.argument(), context, null).type();
|
|
||||||
|
|
||||||
return resolveCallableApplication(
|
|
||||||
applyExpr.span(),
|
|
||||||
applyExpr.callee().span(),
|
|
||||||
callee,
|
|
||||||
argument,
|
|
||||||
context.expectedType(),
|
|
||||||
context.diagnostics());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExprResult resolveCallableApplication(
|
|
||||||
final Span wholeSpan,
|
|
||||||
final Span calleeSpan,
|
|
||||||
final ExprResult callee,
|
|
||||||
final TypeView argumentType,
|
|
||||||
final TypeView expectedType,
|
|
||||||
final DiagnosticSink diagnostics) {
|
|
||||||
final var candidates = callee.callables();
|
|
||||||
if (candidates.isEmpty()) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_NON_CALLABLE_TARGET.name(),
|
|
||||||
"Apply/call target is not callable",
|
|
||||||
calleeSpan);
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
final var compatible = new ArrayList<CallableSymbol>();
|
|
||||||
for (final var candidate : candidates) {
|
|
||||||
if (typeOps.inputCompatible(candidate.inputTypes(), argumentType)) {
|
|
||||||
compatible.add(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compatible.isEmpty()) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_UNRESOLVED_OVERLOAD.name(),
|
|
||||||
"No callable overload matches the provided argument",
|
|
||||||
wholeSpan);
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compatible.size() > 1 && expectedType != null) {
|
|
||||||
final var narrowed = compatible.stream()
|
|
||||||
.filter(candidate -> typeOps.compatible(candidate.outputType(), expectedType))
|
|
||||||
.toList();
|
|
||||||
if (narrowed.size() == 1) {
|
|
||||||
return ExprResult.type(narrowed.getFirst().outputType());
|
|
||||||
}
|
|
||||||
if (!narrowed.isEmpty()) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
|
||||||
"Callable overload resolution remains ambiguous",
|
|
||||||
wholeSpan);
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compatible.size() > 1) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name(),
|
|
||||||
"Callable overload resolution is ambiguous",
|
|
||||||
wholeSpan);
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExprResult.type(compatible.getFirst().outputType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExprResult analyzeMemberExpression(
|
|
||||||
final PbsAst.MemberExpr memberExpr,
|
|
||||||
final PbsFlowExpressionContext context) {
|
|
||||||
final var receiver = analyzeValueExpression(memberExpr.receiver(), context, null).type();
|
|
||||||
final var model = context.model();
|
|
||||||
final var diagnostics = context.diagnostics();
|
|
||||||
final var receiverType = context.receiverType();
|
|
||||||
final var use = context.use();
|
|
||||||
|
|
||||||
if (receiver.kind() == Kind.TYPE_REF) {
|
|
||||||
final var enumCases = model.enums.get(receiver.name());
|
|
||||||
if (enumCases != null && enumCases.contains(memberExpr.memberName())) {
|
|
||||||
return ExprResult.type(TypeView.enumType(receiver.name()));
|
|
||||||
}
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Invalid type member access",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver.kind() == Kind.TUPLE) {
|
|
||||||
if (receiver.tupleFields().size() <= 1) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Member projection is not defined for single-slot carrier values",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
for (final var field : receiver.tupleFields()) {
|
|
||||||
if (memberExpr.memberName().equals(field.label())) {
|
|
||||||
return ExprResult.type(field.type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Tuple label '%s' does not exist".formatted(memberExpr.memberName()),
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver.kind() == Kind.STRUCT) {
|
|
||||||
final var struct = model.structs.get(receiver.name());
|
|
||||||
if (struct == null) {
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
final var field = struct.fields().get(memberExpr.memberName());
|
|
||||||
if (field != null) {
|
|
||||||
if (!canReadStructField(field, receiver, memberExpr.receiver(), receiverType)) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_FIELD_READ_ACCESS_DENIED.name(),
|
|
||||||
"Read access to struct field '%s' is not permitted".formatted(memberExpr.memberName()),
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
return ExprResult.type(field.type());
|
|
||||||
}
|
|
||||||
final var methods = struct.methods().get(memberExpr.memberName());
|
|
||||||
if (methods != null && !methods.isEmpty()) {
|
|
||||||
if (use == ExprUse.VALUE) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
|
||||||
"Bare method extraction is not allowed in PBS core",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
return ExprResult.callables(methods, true);
|
|
||||||
}
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Struct member '%s' does not exist".formatted(memberExpr.memberName()),
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver.kind() == Kind.SERVICE) {
|
|
||||||
final var service = model.services.get(receiver.name());
|
|
||||||
if (service == null) {
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
final var methods = service.methods().get(memberExpr.memberName());
|
|
||||||
if (methods != null && !methods.isEmpty()) {
|
|
||||||
if (use == ExprUse.VALUE) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
|
||||||
"Bare method extraction is not allowed in PBS core",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
return ExprResult.callables(methods, true);
|
|
||||||
}
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Service member '%s' does not exist".formatted(memberExpr.memberName()),
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver.kind() == Kind.CONTRACT) {
|
|
||||||
final var contract = model.contracts.get(receiver.name());
|
|
||||||
if (contract == null) {
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
final var methods = contract.methods().get(memberExpr.memberName());
|
|
||||||
if (methods != null && !methods.isEmpty()) {
|
|
||||||
if (use == ExprUse.VALUE) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name(),
|
|
||||||
"Bare method extraction is not allowed in PBS core",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
return ExprResult.callables(methods, true);
|
|
||||||
}
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Contract member '%s' does not exist".formatted(memberExpr.memberName()),
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
|
||||||
PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name(),
|
|
||||||
"Invalid member access target",
|
|
||||||
memberExpr.span());
|
|
||||||
return ExprResult.type(TypeView.unknown());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canReadStructField(
|
|
||||||
final PbsFlowSemanticSupport.StructFieldInfo field,
|
|
||||||
final TypeView receiver,
|
|
||||||
final PbsAst.Expression receiverExpr,
|
|
||||||
final TypeView currentReceiverType) {
|
|
||||||
if (field.isPublic()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return receiverExpr instanceof PbsAst.ThisExpr
|
|
||||||
&& currentReceiverType != null
|
|
||||||
&& currentReceiverType.kind() == Kind.STRUCT
|
|
||||||
&& receiver.kind() == Kind.STRUCT
|
|
||||||
&& receiver.name().equals(currentReceiverType.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeView analyzeSwitchExpression(
|
private TypeView analyzeSwitchExpression(
|
||||||
final PbsAst.SwitchExpr switchExpr,
|
final PbsAst.SwitchExpr switchExpr,
|
||||||
final PbsFlowExpressionContext context) {
|
final PbsFlowExpressionContext context) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user