diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowCallableResolutionAnalyzer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowCallableResolutionAnalyzer.java new file mode 100644 index 00000000..8c514954 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowCallableResolutionAnalyzer.java @@ -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(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(); + 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(); + 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)); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java index 812042ba..601844c9 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java @@ -33,6 +33,7 @@ final class PbsFlowExpressionAnalyzer { private final PbsFlowTypeOps typeOps; private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer; + private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer; PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) { this.typeOps = typeOps; @@ -40,6 +41,9 @@ final class PbsFlowExpressionAnalyzer { typeOps, this::analyzeExpressionInternal, this::analyzeBlock); + this.callableResolutionAnalyzer = new PbsFlowCallableResolutionAnalyzer( + typeOps, + this::analyzeExpressionInternal); } ExprResult analyzeExpression( @@ -85,15 +89,13 @@ final class PbsFlowExpressionAnalyzer { return structural; } if (expression instanceof PbsAst.MemberExpr memberExpr) { - return analyzeMemberExpression( - memberExpr, - context); + return callableResolutionAnalyzer.analyzeMemberExpression(memberExpr, context); } if (expression instanceof PbsAst.CallExpr callExpr) { - return analyzeCallExpression(callExpr, context); + return callableResolutionAnalyzer.analyzeCallExpression(callExpr, context); } if (expression instanceof PbsAst.ApplyExpr applyExpr) { - return analyzeApplyExpression(applyExpr, context); + return callableResolutionAnalyzer.analyzeApplyExpression(applyExpr, context); } if (expression instanceof PbsAst.IfExpr ifExpr) { final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type(); @@ -162,299 +164,12 @@ final class PbsFlowExpressionAnalyzer { return ExprResult.type(analyzeHandleExpression(handleExpr, context)); } if (expression instanceof PbsAst.BindExpr bindExpr) { - final var contextType = analyzeValueExpression(bindExpr.contextExpression(), context, null).type(); - 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(); - 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 callableResolutionAnalyzer.analyzeBindExpression(bindExpr, context); } 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(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(); - 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( final PbsAst.SwitchExpr switchExpr, final PbsFlowExpressionContext context) {