gotlin/internal/lang/hir.go

765 lines
23 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 {
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
}
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
}
type HIRProgram struct {
Functions []*HIRFunction
Methods []*HIRFunction
}
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.ResolveType(param.Type)
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
}
result, _ := resolver.program.ResolveType(decl.ReturnType)
resolver.resolveStmts(decl.Body, scope, nil, result)
resolver.program.HIR.Functions = append(resolver.program.HIR.Functions, &HIRFunction{Symbol: symbol, Decl: decl, Scope: scope})
}
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 {
typ, _ := resolver.program.ResolveType(param.Type)
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
}
result, _ := resolver.program.ResolveType(method.ReturnType)
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})
}
}
return resolver.err
}
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]}
}
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
}
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
stmts[index] = value
case AddAssignStmt:
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
stmts[index] = value
case MultiAssignStmt:
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)
for _, param := range value.Params {
typ, _ := resolver.program.ResolveType(param.Type)
_ = 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)
resolver.validateEnumExpression(expr, meaning)
if meaning == GoCallExpr && isResultType(expected) {
typ = expected
}
if nullable, ok := typ.(NullableType); ok && typeEqual(nullable.Element, expected) {
typ = expected
}
semantic := &HIRExpr{Type: typ, Meaning: meaning, Symbol: symbol}
semantic.Node = resolver.hirNode(expr, 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 {
return HIRGoCall{Callee: exprMeta(call.Callee), Result: semantic.Type}
}
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 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
}
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) 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 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
}