implements PR-12.3

This commit is contained in:
bQUARKz 2026-03-10 10:16:36 +00:00
parent 8692e94a27
commit 0652c1ab28
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 357 additions and 293 deletions

View File

@ -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));
}
}

View File

@ -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<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 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<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(
final PbsAst.SwitchExpr switchExpr,
final PbsFlowExpressionContext context) {