932 lines
29 KiB
Go
932 lines
29 KiB
Go
package lang
|
|
|
|
import "fmt"
|
|
|
|
type ExprMeaning int
|
|
|
|
const (
|
|
UnresolvedExpr ExprMeaning = iota
|
|
LiteralExpr
|
|
LocalReferenceExpr
|
|
FunctionReferenceExpr
|
|
ClassReferenceExpr
|
|
EnumReferenceExpr
|
|
ImportReferenceExpr
|
|
GotlinCallExpr
|
|
GoCallExpr
|
|
ClassConstructionExpr
|
|
EnumConstructionExpr
|
|
FieldAccessExpr
|
|
MethodCallExpr
|
|
PropagateResultExpr
|
|
MatchValueExpr
|
|
SQLExpression
|
|
MappingExpression
|
|
CoroutineExpression
|
|
)
|
|
|
|
type ExprMeta struct{ Semantic *HIRExpr }
|
|
|
|
type HIRExpr struct {
|
|
ID int
|
|
Type Type
|
|
Meaning ExprMeaning
|
|
Symbol *Symbol
|
|
Node HIRNode
|
|
}
|
|
|
|
type HIRNode interface{ hirNode() }
|
|
|
|
type HIRLiteral struct{}
|
|
|
|
func (HIRLiteral) hirNode() {}
|
|
|
|
type HIRReference struct{ Target *Symbol }
|
|
|
|
func (HIRReference) hirNode() {}
|
|
|
|
type HIRGoCall struct {
|
|
Callee *HIRExpr
|
|
Result Type
|
|
Params []Type
|
|
Variadic bool
|
|
InjectContext bool
|
|
}
|
|
|
|
func (HIRGoCall) hirNode() {}
|
|
|
|
type HIRGotlinCall struct {
|
|
Target *Symbol
|
|
Result Type
|
|
}
|
|
|
|
func (HIRGotlinCall) hirNode() {}
|
|
|
|
type HIRClassConstruction struct{ Class *ClassSymbol }
|
|
|
|
func (HIRClassConstruction) hirNode() {}
|
|
|
|
type HIREnumConstruction struct {
|
|
EnumName, VariantName string
|
|
}
|
|
|
|
func (HIREnumConstruction) hirNode() {}
|
|
|
|
type HIRPropagateResult struct {
|
|
Value *HIRExpr
|
|
Type Type
|
|
}
|
|
|
|
func (HIRPropagateResult) hirNode() {}
|
|
|
|
type HIRMatch struct {
|
|
Value *HIRExpr
|
|
Cases []HIRMatchCase
|
|
Type Type
|
|
}
|
|
|
|
func (HIRMatch) hirNode() {}
|
|
|
|
type HIRMatchCase struct {
|
|
EnumName, VariantName string
|
|
Bindings []string
|
|
Value *HIRExpr
|
|
}
|
|
|
|
type HIRCoroutine struct{ Operation string }
|
|
|
|
func (HIRCoroutine) hirNode() {}
|
|
|
|
type HIRSQL struct{ Type Type }
|
|
|
|
func (HIRSQL) hirNode() {}
|
|
|
|
type HIRMapping struct {
|
|
Source Type
|
|
Target Type
|
|
}
|
|
|
|
func (HIRMapping) hirNode() {}
|
|
|
|
type HIRFunction struct {
|
|
Symbol *Symbol
|
|
Decl *FunctionDecl
|
|
Scope *Scope
|
|
Expression *HIRExpr
|
|
Result Type
|
|
}
|
|
|
|
type HIRProgram struct {
|
|
Functions []*HIRFunction
|
|
Methods []*HIRFunction
|
|
Expressions []*HIRExpr
|
|
}
|
|
|
|
func exprMeta(expr Expr) *HIRExpr {
|
|
switch value := expr.(type) {
|
|
case IdentExpr:
|
|
return value.Meta.Semantic
|
|
case IntExpr:
|
|
return value.Meta.Semantic
|
|
case FloatExpr:
|
|
return value.Meta.Semantic
|
|
case StringExpr:
|
|
return value.Meta.Semantic
|
|
case BoolExpr:
|
|
return value.Meta.Semantic
|
|
case NullExpr:
|
|
return value.Meta.Semantic
|
|
case UnaryExpr:
|
|
return value.Meta.Semantic
|
|
case BinaryExpr:
|
|
return value.Meta.Semantic
|
|
case CallExpr:
|
|
return value.Meta.Semantic
|
|
case SelectorExpr:
|
|
return value.Meta.Semantic
|
|
case SafeSelectorExpr:
|
|
return value.Meta.Semantic
|
|
case NonNullExpr:
|
|
return value.Meta.Semantic
|
|
case TryExpr:
|
|
return value.Meta.Semantic
|
|
case IndexExpr:
|
|
return value.Meta.Semantic
|
|
case EnumVariantExpr:
|
|
return value.Meta.Semantic
|
|
case MatchExpr:
|
|
return value.Meta.Semantic
|
|
case LambdaExpr:
|
|
return value.Meta.Semantic
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func ResolvedType(expr Expr) Type {
|
|
if semantic := exprMeta(expr); semantic != nil {
|
|
return semantic.Type
|
|
}
|
|
return UnknownType{}
|
|
}
|
|
|
|
func ResolvedMeaning(expr Expr) ExprMeaning {
|
|
if semantic := exprMeta(expr); semantic != nil {
|
|
return semantic.Meaning
|
|
}
|
|
return UnresolvedExpr
|
|
}
|
|
|
|
func withExprMeta(expr Expr, semantic *HIRExpr) Expr {
|
|
switch value := expr.(type) {
|
|
case IdentExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case IntExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case FloatExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case StringExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case BoolExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case NullExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case UnaryExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case BinaryExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case CallExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case SelectorExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case SafeSelectorExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case NonNullExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case TryExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case IndexExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case EnumVariantExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case MatchExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
case LambdaExpr:
|
|
value.Meta.Semantic = semantic
|
|
return value
|
|
default:
|
|
return expr
|
|
}
|
|
}
|
|
|
|
type semanticResolver struct {
|
|
program *SemanticProgram
|
|
err error
|
|
}
|
|
|
|
func (resolver *semanticResolver) resolve() error {
|
|
resolver.program.HIR = &HIRProgram{}
|
|
for index := range resolver.program.Syntax.Functions {
|
|
decl := &resolver.program.Syntax.Functions[index]
|
|
symbol, _ := resolver.program.Global.Lookup(decl.Name)
|
|
scope := NewScope(resolver.program.Global)
|
|
for _, param := range decl.Params {
|
|
typ, _ := resolver.program.ResolveTypeRefWithParams(param.TypeRef, decl.TypeParams)
|
|
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
|
}
|
|
result, _ := resolver.program.ResolveTypeRefWithParams(decl.ReturnRef, decl.TypeParams)
|
|
var expression *HIRExpr
|
|
if decl.ExpressionBody != nil {
|
|
expected := result
|
|
if decl.InferReturn {
|
|
expected = UnknownType{}
|
|
}
|
|
decl.ExpressionBody, result = resolver.resolveExpr(decl.ExpressionBody, scope, nil, expected)
|
|
expression = exprMeta(decl.ExpressionBody)
|
|
resolver.updateFunctionResult(symbol, result)
|
|
} else {
|
|
resolver.resolveStmts(decl.Body, scope, nil, result)
|
|
}
|
|
resolver.program.HIR.Functions = append(resolver.program.HIR.Functions, &HIRFunction{Symbol: symbol, Decl: decl, Scope: scope, Expression: expression, Result: result})
|
|
}
|
|
for classIndex := range resolver.program.Syntax.Classes {
|
|
decl := &resolver.program.Syntax.Classes[classIndex]
|
|
class := resolver.program.ClassInfo[decl.Name]
|
|
for methodIndex := range decl.Methods {
|
|
method := &decl.Methods[methodIndex]
|
|
scope := NewScope(resolver.program.Global)
|
|
_ = scope.Define(&Symbol{Name: "this", Kind: VariableSymbol, Type: ClassType{Class: class}})
|
|
for _, param := range method.Params {
|
|
params := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
|
typ, _ := resolver.program.ResolveTypeRefWithParams(param.TypeRef, params)
|
|
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
|
}
|
|
params := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
|
result, _ := resolver.program.ResolveTypeRefWithParams(method.ReturnRef, params)
|
|
var expression *HIRExpr
|
|
if method.ExpressionBody != nil {
|
|
expected := result
|
|
if method.InferReturn {
|
|
expected = UnknownType{}
|
|
}
|
|
method.ExpressionBody, result = resolver.resolveExpr(method.ExpressionBody, scope, class, expected)
|
|
expression = exprMeta(method.ExpressionBody)
|
|
resolver.updateFunctionResult(class.Methods[method.Name], result)
|
|
} else {
|
|
resolver.resolveStmts(method.Body, scope, class, result)
|
|
}
|
|
resolver.program.HIR.Methods = append(resolver.program.HIR.Methods, &HIRFunction{Symbol: class.Methods[method.Name], Decl: method, Scope: scope, Expression: expression, Result: result})
|
|
}
|
|
}
|
|
resolver.inferExpressionReturns()
|
|
return resolver.err
|
|
}
|
|
|
|
func (resolver *semanticResolver) inferExpressionReturns() {
|
|
changed := true
|
|
for changed {
|
|
changed = false
|
|
for _, function := range append(append([]*HIRFunction{}, resolver.program.HIR.Functions...), resolver.program.HIR.Methods...) {
|
|
if function.Decl == nil || !function.Decl.InferReturn || function.Decl.ExpressionBody == nil {
|
|
continue
|
|
}
|
|
var class *ClassSymbol
|
|
if function.Symbol != nil {
|
|
for _, candidate := range resolver.program.ClassInfo {
|
|
if candidate.Method(function.Symbol.Name) == function.Symbol {
|
|
class = candidate
|
|
break
|
|
}
|
|
}
|
|
}
|
|
result := resolver.program.TypeOf(function.Decl.ExpressionBody, TypeEnvironment{Scope: function.Scope, Class: class})
|
|
if isUnknownType(result) {
|
|
continue
|
|
}
|
|
current := function.Result
|
|
if isUnknownType(current) || !typeEqual(current, result) {
|
|
function.Result = result
|
|
if function.Expression != nil {
|
|
function.Expression.Type = result
|
|
}
|
|
resolver.updateFunctionResult(function.Symbol, result)
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) updateFunctionResult(symbol *Symbol, result Type) {
|
|
if symbol == nil {
|
|
return
|
|
}
|
|
if function, ok := symbol.Type.(FunctionType); ok {
|
|
function.Result = result
|
|
symbol.Type = function
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) resolveStmts(stmts []Stmt, scope *Scope, class *ClassSymbol, returnType Type) {
|
|
for index, stmt := range stmts {
|
|
switch value := stmt.(type) {
|
|
case VarDecl:
|
|
expected := Type(UnknownType{})
|
|
if value.Type != "" {
|
|
expected, _ = resolver.program.ResolveType(value.Type)
|
|
}
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
|
|
if isUnknownType(expected) {
|
|
expected = exprMeta(value.Value).Type
|
|
}
|
|
_ = scope.Define(&Symbol{Name: value.Name, Kind: VariableSymbol, Type: expected, Mutable: value.Mutable, Decl: &value})
|
|
stmts[index] = value
|
|
case MultiVarDecl:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
valueTypes := make([]Type, len(value.Names))
|
|
for index := range valueTypes {
|
|
valueTypes[index] = UnknownType{}
|
|
}
|
|
if result, ok := ResolvedType(value.Value).(GenericType); ok && result.Base.String() == "Result" && len(result.Args) == 2 && len(valueTypes) == 2 {
|
|
valueTypes[0] = result.Args[0]
|
|
valueTypes[1] = NullableType{Element: result.Args[1]}
|
|
} else if tuple, ok := ResolvedType(value.Value).(TupleType); ok && len(tuple.Elements) == len(valueTypes) {
|
|
copy(valueTypes, tuple.Elements)
|
|
}
|
|
for index, name := range value.Names {
|
|
_ = scope.Define(&Symbol{Name: name, Kind: VariableSymbol, Type: valueTypes[index], Mutable: value.Mutable})
|
|
}
|
|
stmts[index] = value
|
|
case AssignStmt:
|
|
expected := Type(UnknownType{})
|
|
if symbol, ok := scope.Lookup(value.Name); ok {
|
|
expected = symbol.Type
|
|
} else if class != nil && class.Fields[value.Name] != nil {
|
|
expected = class.Fields[value.Name].Type
|
|
} else {
|
|
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + value.Name, Severity: DiagnosticError, Span: SourceSpan{Start: value.Pos, End: value.Pos + len(value.Name)}})
|
|
}
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
|
|
stmts[index] = value
|
|
case AddAssignStmt:
|
|
_, local := scope.Lookup(value.Name)
|
|
field := false
|
|
if class != nil {
|
|
_, field = class.Fields[value.Name]
|
|
}
|
|
if !local && !field {
|
|
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + value.Name, Severity: DiagnosticError, Span: SourceSpan{Start: value.Pos, End: value.Pos + len(value.Name)}})
|
|
}
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
stmts[index] = value
|
|
case MultiAssignStmt:
|
|
for index, name := range value.Names {
|
|
if _, ok := scope.Lookup(name); !ok {
|
|
position := 0
|
|
if index < len(value.Positions) {
|
|
position = value.Positions[index]
|
|
}
|
|
resolver.addDiagnostic(SemanticDiagnostic{Code: "undefined-variable", Message: "undefined variable " + name, Severity: DiagnosticError, Span: SourceSpan{Start: position, End: position + len(name)}})
|
|
}
|
|
}
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
stmts[index] = value
|
|
case ReturnStmt:
|
|
if value.Value != nil {
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, returnType)
|
|
stmts[index] = value
|
|
}
|
|
case ThrowStmt:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, NamedType{Name: "Error"})
|
|
stmts[index] = value
|
|
case DeferStmt:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, NamedType{Name: "Unit"})
|
|
stmts[index] = value
|
|
case ExprStmt:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
stmts[index] = value
|
|
case IfStmt:
|
|
value.Cond, _ = resolver.resolveExpr(value.Cond, scope, class, NamedType{Name: "Boolean"})
|
|
resolver.resolveStmts(value.Then, NewScope(scope), class, returnType)
|
|
resolver.resolveStmts(value.Else, NewScope(scope), class, returnType)
|
|
stmts[index] = value
|
|
case WhileStmt:
|
|
value.Cond, _ = resolver.resolveExpr(value.Cond, scope, class, NamedType{Name: "Boolean"})
|
|
resolver.resolveStmts(value.Body, NewScope(scope), class, returnType)
|
|
stmts[index] = value
|
|
case ForEachStmt:
|
|
value.Source, _ = resolver.resolveExpr(value.Source, scope, class, UnknownType{})
|
|
bodyScope := NewScope(scope)
|
|
element := collectionElement(exprMeta(value.Source).Type)
|
|
_ = bodyScope.Define(&Symbol{Name: value.Name, Kind: VariableSymbol, Type: element})
|
|
resolver.resolveStmts(value.Body, bodyScope, class, returnType)
|
|
stmts[index] = value
|
|
case MatchStmt:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
for caseIndex := range value.Cases {
|
|
matchCase := &value.Cases[caseIndex]
|
|
caseScope := NewScope(scope)
|
|
resolver.defineMatchBindings(caseScope, matchCase.EnumName, matchCase.VariantName, matchCase.Bindings)
|
|
resolver.resolveStmts(matchCase.Body, caseScope, class, returnType)
|
|
}
|
|
resolver.validateStatementMatch(value)
|
|
stmts[index] = value
|
|
case TryCatchStmt:
|
|
resolver.resolveStmts(value.TryBody, NewScope(scope), class, returnType)
|
|
catchScope := NewScope(scope)
|
|
catchType, _ := resolver.program.ResolveType(value.CatchType)
|
|
_ = catchScope.Define(&Symbol{Name: value.CatchName, Kind: VariableSymbol, Type: catchType})
|
|
resolver.resolveStmts(value.CatchBody, catchScope, class, returnType)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *ClassSymbol, expected Type) (Expr, Type) {
|
|
environment := TypeEnvironment{Scope: scope, Class: class}
|
|
switch value := expr.(type) {
|
|
case UnaryExpr:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
expr = value
|
|
case BinaryExpr:
|
|
value.Left, _ = resolver.resolveExpr(value.Left, scope, class, UnknownType{})
|
|
value.Right, _ = resolver.resolveExpr(value.Right, scope, class, UnknownType{})
|
|
expr = value
|
|
case CallExpr:
|
|
value.Callee, _ = resolver.resolveExpr(value.Callee, scope, class, UnknownType{})
|
|
for index := range value.Args {
|
|
argumentExpected := Type(UnknownType{})
|
|
if selector, ok := value.Callee.(SelectorExpr); ok {
|
|
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "Result" {
|
|
if selector.Name == "Err" {
|
|
argumentExpected = NamedType{Name: "Error"}
|
|
} else if selector.Name == "Ok" {
|
|
if result, ok := expected.(GenericType); ok && result.Base.String() == "Result" && len(result.Args) == 2 {
|
|
argumentExpected = result.Args[0]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
value.Args[index], _ = resolver.resolveExpr(value.Args[index], scope, class, argumentExpected)
|
|
}
|
|
for index := range value.NamedArgs {
|
|
value.NamedArgs[index].Value, _ = resolver.resolveExpr(value.NamedArgs[index].Value, scope, class, UnknownType{})
|
|
}
|
|
expr = value
|
|
case SelectorExpr:
|
|
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
|
expr = value
|
|
case SafeSelectorExpr:
|
|
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
|
expr = value
|
|
case NonNullExpr:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
expr = value
|
|
case TryExpr:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
expr = value
|
|
case IndexExpr:
|
|
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
|
value.Index, _ = resolver.resolveExpr(value.Index, scope, class, NamedType{Name: "Int"})
|
|
expr = value
|
|
case EnumVariantExpr:
|
|
for index := range value.Values {
|
|
value.Values[index], _ = resolver.resolveExpr(value.Values[index], scope, class, UnknownType{})
|
|
}
|
|
expr = value
|
|
case MatchExpr:
|
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
|
for index := range value.Cases {
|
|
matchCase := &value.Cases[index]
|
|
caseScope := NewScope(scope)
|
|
resolver.defineMatchBindings(caseScope, matchCase.EnumName, matchCase.VariantName, matchCase.Bindings)
|
|
matchCase.Value, _ = resolver.resolveExpr(matchCase.Value, caseScope, class, expected)
|
|
}
|
|
resolver.validateValueMatch(value)
|
|
expr = value
|
|
case LambdaExpr:
|
|
lambdaScope := NewScope(scope)
|
|
if value.ImplicitIt {
|
|
_ = lambdaScope.Define(&Symbol{Name: "it", Kind: VariableSymbol, Type: UnknownType{}})
|
|
}
|
|
for _, param := range value.Params {
|
|
typ, _ := resolver.program.ResolveTypeRef(param.TypeRef)
|
|
_ = lambdaScope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
|
}
|
|
resolver.resolveStmts(value.Body, lambdaScope, class, functionResult(expected))
|
|
expr = value
|
|
}
|
|
typ := resolver.program.TypeOf(expr, environment)
|
|
if isUnknownType(typ) && !isUnknownType(expected) {
|
|
typ = expected
|
|
}
|
|
if binary, ok := expr.(BinaryExpr); ok {
|
|
switch binary.Op {
|
|
case "==", "!=", "<", "<=", ">", ">=", "&&", "||":
|
|
typ = NamedType{Name: "Boolean"}
|
|
default:
|
|
if meta := exprMeta(binary.Left); meta != nil {
|
|
typ = meta.Type
|
|
}
|
|
}
|
|
}
|
|
meaning, symbol := resolver.meaning(expr, scope)
|
|
if ident, ok := expr.(IdentExpr); ok && meaning == UnresolvedExpr && isUnknownType(typ) && !isSemanticBuiltin(ident.Name) {
|
|
resolver.addDiagnostic(undefinedDiagnostic(ident.Name, ident.Pos))
|
|
}
|
|
resolver.validateEnumExpression(expr, meaning)
|
|
resolver.validateGenericCall(expr)
|
|
if meaning == GoCallExpr && isResultType(expected) {
|
|
typ = expected
|
|
}
|
|
if nullable, ok := typ.(NullableType); ok && typeEqual(nullable.Element, expected) {
|
|
typ = expected
|
|
}
|
|
semantic := &HIRExpr{ID: len(resolver.program.HIR.Expressions) + 1, Type: typ, Meaning: meaning, Symbol: symbol}
|
|
semantic.Node = resolver.hirNode(expr, semantic)
|
|
resolver.program.HIR.Expressions = append(resolver.program.HIR.Expressions, semantic)
|
|
return withExprMeta(expr, semantic), typ
|
|
}
|
|
|
|
func (resolver *semanticResolver) hirNode(expr Expr, semantic *HIRExpr) HIRNode {
|
|
switch semantic.Meaning {
|
|
case LiteralExpr:
|
|
return HIRLiteral{}
|
|
case LocalReferenceExpr, FunctionReferenceExpr, ClassReferenceExpr, EnumReferenceExpr, ImportReferenceExpr, FieldAccessExpr:
|
|
return HIRReference{Target: semantic.Symbol}
|
|
case GoCallExpr:
|
|
if call, ok := expr.(CallExpr); ok {
|
|
signature, _ := resolver.program.goCallSignature(call)
|
|
return HIRGoCall{Callee: exprMeta(call.Callee), Result: semantic.Type, Params: signature.Params, Variadic: signature.Variadic, InjectContext: shouldInjectCoroutineContext(call, signature)}
|
|
}
|
|
case GotlinCallExpr, MethodCallExpr:
|
|
return HIRGotlinCall{Target: semantic.Symbol, Result: semantic.Type}
|
|
case ClassConstructionExpr:
|
|
if class := classTypeOf(semantic.Type); class != nil {
|
|
return HIRClassConstruction{Class: class}
|
|
}
|
|
case EnumConstructionExpr:
|
|
enumName, variantName := enumExpressionName(expr)
|
|
return HIREnumConstruction{EnumName: enumName, VariantName: variantName}
|
|
case PropagateResultExpr:
|
|
if attempt, ok := expr.(TryExpr); ok {
|
|
return HIRPropagateResult{Value: exprMeta(attempt.Value), Type: semantic.Type}
|
|
}
|
|
case MatchValueExpr:
|
|
if match, ok := expr.(MatchExpr); ok {
|
|
cases := make([]HIRMatchCase, len(match.Cases))
|
|
for index, matchCase := range match.Cases {
|
|
cases[index] = HIRMatchCase{EnumName: matchCase.EnumName, VariantName: matchCase.VariantName, Bindings: matchCase.Bindings, Value: exprMeta(matchCase.Value)}
|
|
}
|
|
return HIRMatch{Value: exprMeta(match.Value), Cases: cases, Type: semantic.Type}
|
|
}
|
|
case CoroutineExpression:
|
|
if call, ok := expr.(CallExpr); ok {
|
|
if ident, ok := call.Callee.(IdentExpr); ok {
|
|
return HIRCoroutine{Operation: ident.Name}
|
|
}
|
|
}
|
|
case SQLExpression:
|
|
return HIRSQL{Type: semantic.Type}
|
|
case MappingExpression:
|
|
if call, ok := expr.(CallExpr); ok {
|
|
if selector, ok := call.Callee.(SelectorExpr); ok {
|
|
target := semantic.Type
|
|
return HIRMapping{Source: ResolvedType(selector.Receiver), Target: target}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func enumExpressionName(expr Expr) (string, string) {
|
|
switch value := expr.(type) {
|
|
case CallExpr:
|
|
if selector, ok := value.Callee.(SelectorExpr); ok {
|
|
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
|
return receiver.Name, selector.Name
|
|
}
|
|
}
|
|
case SelectorExpr:
|
|
if receiver, ok := value.Receiver.(IdentExpr); ok {
|
|
return receiver.Name, value.Name
|
|
}
|
|
case EnumVariantExpr:
|
|
return value.EnumName, value.VariantName
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func (resolver *semanticResolver) meaning(expr Expr, scope *Scope) (ExprMeaning, *Symbol) {
|
|
switch value := expr.(type) {
|
|
case IntExpr, FloatExpr, StringExpr, BoolExpr, NullExpr:
|
|
return LiteralExpr, nil
|
|
case IdentExpr:
|
|
if symbol, ok := scope.Lookup(value.Name); ok {
|
|
switch symbol.Kind {
|
|
case FunctionSymbolKind:
|
|
return FunctionReferenceExpr, symbol
|
|
case ClassSymbolKind:
|
|
return ClassReferenceExpr, symbol
|
|
case EnumSymbolKind:
|
|
return EnumReferenceExpr, symbol
|
|
case ImportSymbolKind:
|
|
return ImportReferenceExpr, symbol
|
|
default:
|
|
return LocalReferenceExpr, symbol
|
|
}
|
|
}
|
|
case CallExpr:
|
|
if _, _, _, ok := splitSQLChain(value); ok {
|
|
return SQLExpression, nil
|
|
}
|
|
if _, ok := value.Callee.(SelectorExpr); ok {
|
|
selector := value.Callee.(SelectorExpr)
|
|
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "json" && selector.Name == "decode" {
|
|
return GotlinCallExpr, nil
|
|
}
|
|
if selector.Name == "mapTo" {
|
|
return MappingExpression, nil
|
|
}
|
|
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
|
if pack := resolver.program.Packages[receiver.Name]; pack != nil {
|
|
if function := pack.Function(selector.Name); function != nil {
|
|
return GotlinCallExpr, function
|
|
}
|
|
}
|
|
if receiver.Name == "Result" {
|
|
return EnumConstructionExpr, nil
|
|
}
|
|
if _, ok := resolver.program.Enums[receiver.Name]; ok {
|
|
return EnumConstructionExpr, nil
|
|
}
|
|
}
|
|
if root, ok := selectorRootAlias(selector); ok && resolver.program.Imports[root] {
|
|
return GoCallExpr, nil
|
|
}
|
|
if class, _ := classInstance(ResolvedType(selector.Receiver)); class != nil {
|
|
if method := class.Method(selector.Name); method != nil {
|
|
return MethodCallExpr, method
|
|
}
|
|
}
|
|
receiverType := ResolvedType(selector.Receiver)
|
|
if !isUnknownType(resolver.program.goMethodType(receiverType, selector.Name)) {
|
|
return GoCallExpr, nil
|
|
}
|
|
return MethodCallExpr, nil
|
|
}
|
|
if ident, ok := value.Callee.(IdentExpr); ok {
|
|
if coroutineBuiltins[ident.Name] {
|
|
return CoroutineExpression, nil
|
|
}
|
|
if symbol, ok := resolver.program.Global.Lookup(ident.Name); ok {
|
|
if symbol.Kind == ClassSymbolKind {
|
|
return ClassConstructionExpr, symbol
|
|
}
|
|
if symbol.Kind == FunctionSymbolKind {
|
|
return GotlinCallExpr, symbol
|
|
}
|
|
}
|
|
}
|
|
return GoCallExpr, nil
|
|
case SelectorExpr:
|
|
if receiver, ok := value.Receiver.(IdentExpr); ok {
|
|
if _, ok := resolver.program.Enums[receiver.Name]; ok {
|
|
return EnumReferenceExpr, nil
|
|
}
|
|
}
|
|
return FieldAccessExpr, nil
|
|
case SafeSelectorExpr:
|
|
return FieldAccessExpr, nil
|
|
case EnumVariantExpr:
|
|
return EnumConstructionExpr, nil
|
|
case TryExpr:
|
|
return PropagateResultExpr, nil
|
|
case MatchExpr:
|
|
return MatchValueExpr, nil
|
|
}
|
|
return UnresolvedExpr, nil
|
|
}
|
|
|
|
func (resolver *semanticResolver) defineMatchBindings(scope *Scope, enumName, variantName string, bindings []string) {
|
|
decl, ok := resolver.program.Enums[enumName]
|
|
if !ok {
|
|
return
|
|
}
|
|
variant := enumVariant(decl, variantName)
|
|
if variant == nil {
|
|
return
|
|
}
|
|
for index, binding := range bindings {
|
|
if index >= len(variant.PayloadTypes) {
|
|
break
|
|
}
|
|
typ, _ := resolver.program.ResolveType(variant.PayloadTypes[index])
|
|
_ = scope.Define(&Symbol{Name: binding, Kind: VariableSymbol, Type: typ})
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) validateStatementMatch(match MatchStmt) {
|
|
patterns := make([]matchPattern, len(match.Cases))
|
|
for index, matchCase := range match.Cases {
|
|
patterns[index] = matchPattern{enumName: matchCase.EnumName, variantName: matchCase.VariantName, bindings: matchCase.Bindings}
|
|
}
|
|
resolver.validateMatchPatterns(patterns)
|
|
}
|
|
|
|
func (resolver *semanticResolver) validateValueMatch(match MatchExpr) {
|
|
patterns := make([]matchPattern, len(match.Cases))
|
|
var result Type = UnknownType{}
|
|
for index, matchCase := range match.Cases {
|
|
patterns[index] = matchPattern{enumName: matchCase.EnumName, variantName: matchCase.VariantName, bindings: matchCase.Bindings}
|
|
armType := ResolvedType(matchCase.Value)
|
|
if isUnknownType(result) {
|
|
result = armType
|
|
} else if !isUnknownType(armType) && !typeEqual(result, armType) {
|
|
resolver.fail(fmt.Errorf("match expression arm %s.%s has type %s, expected %s", matchCase.EnumName, matchCase.VariantName, armType.String(), result.String()))
|
|
}
|
|
}
|
|
if isUnknownType(result) {
|
|
resolver.fail(fmt.Errorf("match expression result type cannot be inferred"))
|
|
}
|
|
resolver.validateMatchPatterns(patterns)
|
|
}
|
|
|
|
type matchPattern struct {
|
|
enumName, variantName string
|
|
bindings []string
|
|
}
|
|
|
|
func (resolver *semanticResolver) validateMatchPatterns(patterns []matchPattern) {
|
|
if len(patterns) == 0 {
|
|
resolver.fail(fmt.Errorf("match requires at least one case"))
|
|
return
|
|
}
|
|
enumName := patterns[0].enumName
|
|
decl, ok := resolver.program.Enums[enumName]
|
|
if !ok {
|
|
resolver.fail(fmt.Errorf("match value is not a known enum"))
|
|
return
|
|
}
|
|
seen := map[string]bool{}
|
|
for _, pattern := range patterns {
|
|
if pattern.enumName != enumName {
|
|
resolver.fail(fmt.Errorf("match case %s.%s does not match enum %s", pattern.enumName, pattern.variantName, enumName))
|
|
return
|
|
}
|
|
if seen[pattern.variantName] {
|
|
resolver.fail(fmt.Errorf("duplicate match case %s.%s", enumName, pattern.variantName))
|
|
return
|
|
}
|
|
seen[pattern.variantName] = true
|
|
variant := enumVariant(decl, pattern.variantName)
|
|
if variant == nil {
|
|
resolver.fail(fmt.Errorf("unknown variant %s.%s", enumName, pattern.variantName))
|
|
return
|
|
}
|
|
if len(pattern.bindings) != len(variant.PayloadTypes) {
|
|
resolver.fail(fmt.Errorf("match case %s.%s expects %d bindings", enumName, pattern.variantName, len(variant.PayloadTypes)))
|
|
return
|
|
}
|
|
}
|
|
for _, variant := range decl.Variants {
|
|
if !seen[variant.Name] {
|
|
resolver.fail(fmt.Errorf("non-exhaustive match for %s: missing %s", enumName, variant.Name))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) fail(err error) {
|
|
if resolver.err == nil {
|
|
resolver.err = err
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) addDiagnostic(diagnostic SemanticDiagnostic) {
|
|
for _, existing := range resolver.program.Diagnostics {
|
|
if existing.Code == diagnostic.Code && existing.Span.Start == diagnostic.Span.Start && existing.Message == diagnostic.Message {
|
|
return
|
|
}
|
|
}
|
|
resolver.program.Diagnostics = append(resolver.program.Diagnostics, diagnostic)
|
|
}
|
|
|
|
func isSemanticBuiltin(name string) bool {
|
|
return semanticBuiltins[name]
|
|
}
|
|
|
|
var semanticBuiltins = map[string]bool{
|
|
"println": true, "runCatching": true, "Channel": true,
|
|
"listOf": true, "mutableListOf": true, "mapOf": true, "mutableMapOf": true,
|
|
"append": true, "keys": true, "goAssert": true, "len": true, "cap": true,
|
|
"make": true, "new": true, "copy": true, "delete": true, "close": true,
|
|
"panic": true, "recover": true, "string": true, "int": true, "float64": true, "bool": true,
|
|
"sql": true, "set": true, "now": true, "Result": true, "ByteSlice": true,
|
|
"runBlocking": true, "withContext": true, "coroutineScope": true, "launch": true, "async": true,
|
|
"delay": true, "withTimeout": true, "isActive": true, "coroutineContext": true,
|
|
"continue": true, "break": true,
|
|
}
|
|
|
|
func (resolver *semanticResolver) validateEnumExpression(expr Expr, meaning ExprMeaning) {
|
|
if meaning != EnumConstructionExpr {
|
|
return
|
|
}
|
|
var enumName, variantName string
|
|
valueCount := 0
|
|
switch value := expr.(type) {
|
|
case CallExpr:
|
|
selector, ok := value.Callee.(SelectorExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
receiver, ok := selector.Receiver.(IdentExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
enumName, variantName, valueCount = receiver.Name, selector.Name, len(value.Args)
|
|
case SelectorExpr:
|
|
receiver, ok := value.Receiver.(IdentExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
enumName, variantName = receiver.Name, value.Name
|
|
default:
|
|
return
|
|
}
|
|
if enumName == "Result" {
|
|
if valueCount != 1 {
|
|
resolver.fail(fmt.Errorf("Result.%s expects one value", variantName))
|
|
}
|
|
return
|
|
}
|
|
decl, ok := resolver.program.Enums[enumName]
|
|
if !ok {
|
|
return
|
|
}
|
|
variant := enumVariant(decl, variantName)
|
|
if variant == nil {
|
|
resolver.fail(fmt.Errorf("unknown variant %s.%s", enumName, variantName))
|
|
return
|
|
}
|
|
if len(variant.PayloadTypes) != valueCount {
|
|
resolver.fail(fmt.Errorf("variant %s.%s expects %d values", enumName, variantName, len(variant.PayloadTypes)))
|
|
}
|
|
}
|
|
|
|
func (resolver *semanticResolver) validateGenericCall(expr Expr) {
|
|
call, ok := expr.(CallExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
ident, ok := call.Callee.(IdentExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
var params []string
|
|
found := false
|
|
if function, ok := resolver.program.Functions[ident.Name]; ok {
|
|
params = function.TypeParams
|
|
found = true
|
|
}
|
|
if class, ok := resolver.program.ClassInfo[ident.Name]; ok {
|
|
params = class.TypeParams
|
|
found = true
|
|
}
|
|
if found && len(call.TypeArgs) > 0 && len(call.TypeArgs) != len(params) {
|
|
resolver.fail(fmt.Errorf("%s expects %d type arguments, got %d", ident.Name, len(params), len(call.TypeArgs)))
|
|
}
|
|
}
|
|
|
|
func collectionElement(typ Type) Type {
|
|
if generic, ok := typ.(GenericType); ok && len(generic.Args) > 0 {
|
|
return generic.Args[len(generic.Args)-1]
|
|
}
|
|
return UnknownType{}
|
|
}
|
|
|
|
func functionResult(typ Type) Type {
|
|
if function, ok := typ.(FunctionType); ok {
|
|
return function.Result
|
|
}
|
|
return UnknownType{}
|
|
}
|
|
|
|
func isResultType(typ Type) bool {
|
|
result, ok := typ.(GenericType)
|
|
return ok && result.Base.String() == "Result" && len(result.Args) == 2
|
|
}
|