Infer final expression returns in lambdas
This commit is contained in:
parent
773c34f3f4
commit
72606e7818
6 changed files with 300 additions and 7 deletions
|
|
@ -27,6 +27,7 @@ across package boundaries.
|
||||||
- `if`, `else`, `while`, and `for (item in items)`
|
- `if`, `else`, `while`, and `for (item in items)`
|
||||||
- function calls
|
- function calls
|
||||||
- lambdas like `{ x: Int -> println(x) }` and `{ println(it) }`
|
- lambdas like `{ x: Int -> println(x) }` and `{ println(it) }`
|
||||||
|
- Kotlin-style final-expression returns in value lambdas, for example `{ value -> value * 2 }`
|
||||||
- `class` with primary-constructor fields and methods
|
- `class` with primary-constructor fields and methods
|
||||||
- `interface` with method signatures
|
- `interface` with method signatures
|
||||||
- Rust-style algebraic `enum` declarations with payload variants and exhaustive `match`
|
- Rust-style algebraic `enum` declarations with payload variants and exhaustive `match`
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,59 @@ fun handle(request: *http.Request) = withContext(request.context()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLambdaReturnsFinalExpression(t *testing.T) {
|
||||||
|
program, err := Parse(`package demo
|
||||||
|
fun transform<T, R>(value: T, block: (T) -> R): R = block(value)
|
||||||
|
fun answer(): Int = transform(21) { value -> value * 2 }`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
output, err := GenerateGo(program)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
code := string(output)
|
||||||
|
for _, expected := range []string{"func(value int) int", "return value * 2", "func answer() int"} {
|
||||||
|
if !strings.Contains(code, expected) {
|
||||||
|
t.Fatalf("missing %q:\n%s", expected, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoCallbackReturnsFinalExpression(t *testing.T) {
|
||||||
|
program, err := Parse(`package demo
|
||||||
|
import sort
|
||||||
|
fun order(values: MutableList<Int>) {
|
||||||
|
sort.slice(values) { left, right -> values[left] < values[right] }
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
output, err := GenerateGo(program)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
code := string(output)
|
||||||
|
for _, expected := range []string{"func(left int, right int) bool", "return values[left] < values[right]"} {
|
||||||
|
if !strings.Contains(code, expected) {
|
||||||
|
t.Fatalf("missing %q:\n%s", expected, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnitLambdaKeepsFinalExpressionAsStatement(t *testing.T) {
|
||||||
|
program, err := Parse(`package demo
|
||||||
|
fun execute(block: () -> Unit) = block()
|
||||||
|
fun start() = execute { println("started") }`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
output, err := GenerateGo(program)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.Contains(string(output), `return fmt.Println("started")`) {
|
||||||
|
t.Fatalf("Unit lambda returned final expression:\n%s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ func (g *goGenerator) stmt(stmt Stmt, tail []Stmt) error {
|
||||||
} else {
|
} else {
|
||||||
value, err := g.expr(s.Value, g.currentFunc.ReturnType)
|
value, err := g.expr(s.Value, g.currentFunc.ReturnType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("returning %s: %w", g.currentFunc.ReturnType, err)
|
||||||
}
|
}
|
||||||
value = g.passthroughValue(s.Value, value)
|
value = g.passthroughValue(s.Value, value)
|
||||||
g.line(fmt.Sprintf("return %s", value))
|
g.line(fmt.Sprintf("return %s", value))
|
||||||
|
|
@ -1210,12 +1210,15 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0, len(e.Args))
|
args := make([]string, 0, len(e.Args))
|
||||||
argTypes := g.callArgTypes(e.Callee, len(e.Args))
|
argTypes := g.callArgTypes(e)
|
||||||
for i, arg := range e.Args {
|
for i, arg := range e.Args {
|
||||||
argType := ""
|
argType := ""
|
||||||
if i < len(argTypes) {
|
if i < len(argTypes) {
|
||||||
argType = argTypes[i]
|
argType = argTypes[i]
|
||||||
}
|
}
|
||||||
|
if _, isNull := arg.(NullExpr); isNull && g.goCallArgumentIsNilable(e, i) {
|
||||||
|
argType = ""
|
||||||
|
}
|
||||||
value, err := g.expr(arg, argType)
|
value, err := g.expr(arg, argType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -2162,7 +2165,23 @@ func (g *goGenerator) lambda(lambda LambdaExpr, expectedType string) (string, er
|
||||||
for _, param := range params {
|
for _, param := range params {
|
||||||
sub.defineType(param.Name, param.Type)
|
sub.defineType(param.Name, param.Type)
|
||||||
}
|
}
|
||||||
if err := sub.block(lambda.Body); err != nil {
|
if returnType != "" && returnType != "Unit" && !lambdaHasValueReturn(lambda.Body) {
|
||||||
|
if len(lambda.Body) == 0 {
|
||||||
|
return "", fmt.Errorf("value lambda requires a final expression")
|
||||||
|
}
|
||||||
|
last, ok := lambda.Body[len(lambda.Body)-1].(ExprStmt)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("value lambda requires a final expression or explicit return")
|
||||||
|
}
|
||||||
|
if err := sub.block(lambda.Body[:len(lambda.Body)-1]); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
value, err := sub.expr(last.Value, returnType)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sub.line("return " + value)
|
||||||
|
} else if err := sub.block(lambda.Body); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
b.Write(sub.buf.Bytes())
|
b.Write(sub.buf.Bytes())
|
||||||
|
|
@ -2170,7 +2189,50 @@ func (g *goGenerator) lambda(lambda LambdaExpr, expectedType string) (string, er
|
||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *goGenerator) callArgTypes(callee Expr, argCount int) []string {
|
func (g *goGenerator) callArgTypes(call CallExpr) []string {
|
||||||
|
argCount := len(call.Args)
|
||||||
|
if resolved := exprMeta(call); resolved != nil {
|
||||||
|
switch node := resolved.Node.(type) {
|
||||||
|
case HIRGoCall:
|
||||||
|
if len(node.Params) > 0 {
|
||||||
|
params := node.Params
|
||||||
|
if node.InjectContext {
|
||||||
|
params = params[1:]
|
||||||
|
}
|
||||||
|
return semanticParamStrings(params, argCount, node.Variadic)
|
||||||
|
}
|
||||||
|
case HIRGotlinCall:
|
||||||
|
if node.Target != nil {
|
||||||
|
if function, ok := node.Target.Type.(FunctionType); ok {
|
||||||
|
bindings := map[string]Type{}
|
||||||
|
for index, argument := range call.TypeArgs {
|
||||||
|
if index < len(function.TypeParams) {
|
||||||
|
typ, _ := g.semantic.ResolveType(argument)
|
||||||
|
bindings[function.TypeParams[index]] = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if selector, ok := call.Callee.(SelectorExpr); ok {
|
||||||
|
if _, receiverBindings := classInstance(ResolvedType(selector.Receiver)); receiverBindings != nil {
|
||||||
|
for name, typ := range receiverBindings {
|
||||||
|
bindings[name] = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index, argument := range call.Args {
|
||||||
|
if index < len(function.Params) {
|
||||||
|
inferTypeBindings(function.Params[index], ResolvedType(argument), bindings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params := make([]Type, len(function.Params))
|
||||||
|
for index, param := range function.Params {
|
||||||
|
params[index] = substituteType(param, bindings)
|
||||||
|
}
|
||||||
|
return semanticParamStrings(params, argCount, function.Variadic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callee := call.Callee
|
||||||
ident, ok := callee.(IdentExpr)
|
ident, ok := callee.(IdentExpr)
|
||||||
if ok {
|
if ok {
|
||||||
if ident.Name == "runCatching" {
|
if ident.Name == "runCatching" {
|
||||||
|
|
@ -2192,7 +2254,6 @@ func (g *goGenerator) callArgTypes(callee Expr, argCount int) []string {
|
||||||
}
|
}
|
||||||
return argTypes
|
return argTypes
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selector, ok := selectorPath(callee)
|
selector, ok := selectorPath(callee)
|
||||||
|
|
@ -2209,6 +2270,63 @@ func (g *goGenerator) callArgTypes(callee Expr, argCount int) []string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func semanticParamStrings(params []Type, argCount int, variadic bool) []string {
|
||||||
|
result := make([]string, 0, argCount)
|
||||||
|
for index := 0; index < argCount; index++ {
|
||||||
|
paramIndex := index
|
||||||
|
if paramIndex >= len(params) {
|
||||||
|
if !variadic || len(params) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
paramIndex = len(params) - 1
|
||||||
|
}
|
||||||
|
param := params[paramIndex]
|
||||||
|
if variadic && paramIndex == len(params)-1 {
|
||||||
|
if list, ok := param.(GenericType); ok && (list.Base.String() == "List" || list.Base.String() == "MutableList") && len(list.Args) == 1 {
|
||||||
|
param = list.Args[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, param.String())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *goGenerator) goCallArgumentIsNilable(call CallExpr, index int) bool {
|
||||||
|
resolved := exprMeta(call)
|
||||||
|
if resolved == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
goCall, ok := resolved.Node.(HIRGoCall)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
params := goCall.Params
|
||||||
|
if goCall.InjectContext && len(params) > 0 {
|
||||||
|
params = params[1:]
|
||||||
|
}
|
||||||
|
if len(params) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
paramIndex := index
|
||||||
|
if paramIndex >= len(params) {
|
||||||
|
if !goCall.Variadic {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
paramIndex = len(params) - 1
|
||||||
|
}
|
||||||
|
param := params[paramIndex]
|
||||||
|
if goCall.Variadic && paramIndex == len(params)-1 {
|
||||||
|
if list, ok := param.(GenericType); ok && len(list.Args) == 1 {
|
||||||
|
param = list.Args[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch param.(type) {
|
||||||
|
case GoInterfaceType, GoPointerType, NullableType:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func nameUsedInStmts(name string, stmts []Stmt) bool {
|
func nameUsedInStmts(name string, stmts []Stmt) bool {
|
||||||
return nameUsedInStmtsWithShadow(name, stmts, false)
|
return nameUsedInStmtsWithShadow(name, stmts, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ func semanticTypeFromGoResults(results *gotypes.Tuple) Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
func semanticTypeFromGo(typ gotypes.Type) Type {
|
func semanticTypeFromGo(typ gotypes.Type) Type {
|
||||||
|
typ = gotypes.Unalias(typ)
|
||||||
switch value := typ.(type) {
|
switch value := typ.(type) {
|
||||||
case *gotypes.Basic:
|
case *gotypes.Basic:
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
|
|
@ -210,6 +211,9 @@ func semanticTypeFromGo(typ gotypes.Type) Type {
|
||||||
return NamedType{Name: "Error"}
|
return NamedType{Name: "Error"}
|
||||||
}
|
}
|
||||||
if value.Obj().Pkg() != nil {
|
if value.Obj().Pkg() != nil {
|
||||||
|
if _, ok := value.Underlying().(*gotypes.Interface); ok {
|
||||||
|
return GoInterfaceType{Name: value.Obj().Pkg().Name() + "." + value.Obj().Name()}
|
||||||
|
}
|
||||||
return NamedType{Name: value.Obj().Pkg().Name() + "." + value.Obj().Name()}
|
return NamedType{Name: value.Obj().Pkg().Name() + "." + value.Obj().Name()}
|
||||||
}
|
}
|
||||||
return NamedType{Name: value.Obj().Name()}
|
return NamedType{Name: value.Obj().Name()}
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,7 @@ func (resolver *semanticResolver) resolveStmts(stmts []Stmt, scope *Scope, class
|
||||||
|
|
||||||
func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *ClassSymbol, expected Type) (Expr, Type) {
|
func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *ClassSymbol, expected Type) (Expr, Type) {
|
||||||
environment := TypeEnvironment{Scope: scope, Class: class}
|
environment := TypeEnvironment{Scope: scope, Class: class}
|
||||||
|
var typeOverride Type
|
||||||
switch value := expr.(type) {
|
switch value := expr.(type) {
|
||||||
case UnaryExpr:
|
case UnaryExpr:
|
||||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||||
|
|
@ -468,8 +469,26 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
||||||
expr = value
|
expr = value
|
||||||
case CallExpr:
|
case CallExpr:
|
||||||
value.Callee, _ = resolver.resolveExpr(value.Callee, scope, class, UnknownType{})
|
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 {
|
for index := range value.Args {
|
||||||
argumentExpected := Type(UnknownType{})
|
argumentExpected := Type(UnknownType{})
|
||||||
|
if hasSignature && index < len(params) {
|
||||||
|
argumentExpected = substituteType(params[index], bindings)
|
||||||
|
}
|
||||||
if selector, ok := value.Callee.(SelectorExpr); ok {
|
if selector, ok := value.Callee.(SelectorExpr); ok {
|
||||||
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "Result" {
|
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "Result" {
|
||||||
if selector.Name == "Err" {
|
if selector.Name == "Err" {
|
||||||
|
|
@ -482,6 +501,9 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value.Args[index], _ = resolver.resolveExpr(value.Args[index], scope, class, argumentExpected)
|
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 {
|
for index := range value.NamedArgs {
|
||||||
value.NamedArgs[index].Value, _ = resolver.resolveExpr(value.NamedArgs[index].Value, scope, class, UnknownType{})
|
value.NamedArgs[index].Value, _ = resolver.resolveExpr(value.NamedArgs[index].Value, scope, class, UnknownType{})
|
||||||
|
|
@ -520,17 +542,40 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
||||||
expr = value
|
expr = value
|
||||||
case LambdaExpr:
|
case LambdaExpr:
|
||||||
lambdaScope := NewScope(scope)
|
lambdaScope := NewScope(scope)
|
||||||
|
functionExpected, hasFunctionExpected := expected.(FunctionType)
|
||||||
if value.ImplicitIt {
|
if value.ImplicitIt {
|
||||||
_ = lambdaScope.Define(&Symbol{Name: "it", Kind: VariableSymbol, Type: UnknownType{}})
|
itType := Type(UnknownType{})
|
||||||
|
if hasFunctionExpected && len(functionExpected.Params) == 1 {
|
||||||
|
itType = functionExpected.Params[0]
|
||||||
}
|
}
|
||||||
for _, param := range value.Params {
|
_ = lambdaScope.Define(&Symbol{Name: "it", Kind: VariableSymbol, Type: itType})
|
||||||
|
}
|
||||||
|
for index, param := range value.Params {
|
||||||
typ, _ := resolver.program.ResolveTypeRef(param.TypeRef)
|
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})
|
_ = lambdaScope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||||
}
|
}
|
||||||
resolver.resolveStmts(value.Body, lambdaScope, class, functionResult(expected))
|
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
|
expr = value
|
||||||
}
|
}
|
||||||
typ := resolver.program.TypeOf(expr, environment)
|
typ := resolver.program.TypeOf(expr, environment)
|
||||||
|
if typeOverride != nil {
|
||||||
|
typ = typeOverride
|
||||||
|
}
|
||||||
if isUnknownType(typ) && !isUnknownType(expected) {
|
if isUnknownType(typ) && !isUnknownType(expected) {
|
||||||
typ = expected
|
typ = expected
|
||||||
}
|
}
|
||||||
|
|
@ -562,6 +607,57 @@ func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *Cl
|
||||||
return withExprMeta(expr, semantic), typ
|
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 {
|
func (resolver *semanticResolver) hirNode(expr Expr, semantic *HIRExpr) HIRNode {
|
||||||
switch semantic.Meaning {
|
switch semantic.Meaning {
|
||||||
case LiteralExpr:
|
case LiteralExpr:
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,11 @@ type ImportedClassType struct {
|
||||||
func (ImportedClassType) typeNode() {}
|
func (ImportedClassType) typeNode() {}
|
||||||
func (t ImportedClassType) String() string { return t.Package + "." + t.Class.Name }
|
func (t ImportedClassType) String() string { return t.Package + "." + t.Class.Name }
|
||||||
|
|
||||||
|
type GoInterfaceType struct{ Name string }
|
||||||
|
|
||||||
|
func (GoInterfaceType) typeNode() {}
|
||||||
|
func (t GoInterfaceType) String() string { return t.Name }
|
||||||
|
|
||||||
type NullableType struct{ Element Type }
|
type NullableType struct{ Element Type }
|
||||||
|
|
||||||
func (NullableType) typeNode() {}
|
func (NullableType) typeNode() {}
|
||||||
|
|
@ -330,6 +335,17 @@ func inferTypeBindings(parameter, argument Type, bindings map[string]Type) {
|
||||||
inferTypeBindings(expected.Args[index], actual.Args[index], bindings)
|
inferTypeBindings(expected.Args[index], actual.Args[index], bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case FunctionType:
|
||||||
|
actual, ok := argument.(FunctionType)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for index := range expected.Params {
|
||||||
|
if index < len(actual.Params) {
|
||||||
|
inferTypeBindings(expected.Params[index], actual.Params[index], bindings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inferTypeBindings(expected.Result, actual.Result, bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,6 +357,8 @@ func renderGoType(typ Type) string {
|
||||||
return "*" + value.Class.Name
|
return "*" + value.Class.Name
|
||||||
case ImportedClassType:
|
case ImportedClassType:
|
||||||
return "*" + value.Package + "." + value.Class.Name
|
return "*" + value.Package + "." + value.Class.Name
|
||||||
|
case GoInterfaceType:
|
||||||
|
return value.Name
|
||||||
case NullableType:
|
case NullableType:
|
||||||
element := renderGoType(value.Element)
|
element := renderGoType(value.Element)
|
||||||
switch value.Element.(type) {
|
switch value.Element.(type) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue