gotlin/internal/lang/hir.go

1028 lines
33 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}
var typeOverride Type
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{})
signature, hasSignature := resolver.callSignature(value, environment)
params := signature.Params
if hasSignature && len(params) > 0 && params[0].String() == "context.Context" {
explicitContext := len(value.Args) > 0 && resolver.program.TypeOf(value.Args[0], environment).String() == "context.Context"
if !explicitContext && contextCanBeOmitted(len(value.Args), len(params), signature.Variadic) {
params = params[1:]
}
}
bindings := map[string]Type{}
for index, argument := range value.TypeArgs {
if index < len(signature.TypeParams) {
typ, _ := resolver.program.ResolveType(argument)
bindings[signature.TypeParams[index]] = typ
}
}
for index := range value.Args {
argumentExpected := Type(UnknownType{})
if hasSignature && index < len(params) {
argumentExpected = substituteType(params[index], bindings)
}
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)
if hasSignature && index < len(params) {
inferTypeBindings(params[index], ResolvedType(value.Args[index]), bindings)
}
}
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)
functionExpected, hasFunctionExpected := expected.(FunctionType)
if value.ImplicitIt {
itType := Type(UnknownType{})
if hasFunctionExpected && len(functionExpected.Params) == 1 {
itType = functionExpected.Params[0]
}
_ = lambdaScope.Define(&Symbol{Name: "it", Kind: VariableSymbol, Type: itType})
}
for index, param := range value.Params {
typ, _ := resolver.program.ResolveTypeRef(param.TypeRef)
if isUnknownType(typ) && hasFunctionExpected && index < len(functionExpected.Params) {
typ = functionExpected.Params[index]
}
_ = lambdaScope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
}
resolver.resolveStmts(value.Body, lambdaScope, class, functionResult(expected))
if hasFunctionExpected {
result := functionExpected.Result
if !lambdaHasValueReturn(value.Body) && len(value.Body) > 0 {
if final, ok := value.Body[len(value.Body)-1].(ExprStmt); ok {
actual := ResolvedType(final.Value)
if !isUnknownType(actual) {
result = actual
}
}
}
typeOverride = FunctionType{Params: functionExpected.Params, Result: result, Effects: functionExpected.Effects}
}
expr = value
}
typ := resolver.program.TypeOf(expr, environment)
if typeOverride != nil {
typ = typeOverride
}
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) callSignature(call CallExpr, environment TypeEnvironment) (FunctionType, bool) {
if signature, ok := resolver.program.goCallSignature(call); ok {
return signature, true
}
switch callee := call.Callee.(type) {
case IdentExpr:
if symbol, ok := resolver.program.Global.Lookup(callee.Name); ok {
if signature, ok := symbol.Type.(FunctionType); ok {
return signature, true
}
}
if class := resolver.program.ClassInfo[callee.Name]; class != nil {
params := make([]Type, len(class.Decl.Fields))
for index, field := range class.Decl.Fields {
params[index] = class.Fields[field.Name].Type
}
return FunctionType{TypeParams: class.TypeParams, Params: params, Result: ClassType{Class: class}}, true
}
case SelectorExpr:
if receiver, ok := callee.Receiver.(IdentExpr); ok {
if pack := resolver.program.Packages[receiver.Name]; pack != nil {
if function := pack.Function(callee.Name); function != nil {
if signature, ok := function.Type.(FunctionType); ok {
return signature, true
}
}
}
}
if class, bindings := classInstance(resolver.program.TypeOf(callee.Receiver, environment)); class != nil {
if method := class.Method(callee.Name); method != nil {
if signature, ok := method.Type.(FunctionType); ok {
substituted := substituteType(signature, bindings).(FunctionType)
return substituted, true
}
}
}
}
return FunctionType{}, false
}
func contextCanBeOmitted(argumentCount, parameterCount int, variadic bool) bool {
if variadic {
minimum := parameterCount - 2
if minimum < 0 {
minimum = 0
}
return argumentCount >= minimum
}
return argumentCount == parameterCount-1
}
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
}