Add expression functions with context boundaries
This commit is contained in:
parent
5085ae51aa
commit
773c34f3f4
18 changed files with 391 additions and 52 deletions
|
|
@ -60,20 +60,24 @@ type FieldDecl struct {
|
|||
}
|
||||
|
||||
type FunctionSignature struct {
|
||||
Name string
|
||||
TypeParams []string
|
||||
Params []Param
|
||||
ReturnType string
|
||||
ReturnRef TypeRef
|
||||
Name string
|
||||
TypeParams []string
|
||||
Params []Param
|
||||
ReturnType string
|
||||
ReturnRef TypeRef
|
||||
ReturnExplicit bool
|
||||
}
|
||||
|
||||
type FunctionDecl struct {
|
||||
Name string
|
||||
TypeParams []string
|
||||
Params []Param
|
||||
ReturnType string
|
||||
ReturnRef TypeRef
|
||||
Body []Stmt
|
||||
Name string
|
||||
TypeParams []string
|
||||
Params []Param
|
||||
ReturnType string
|
||||
ReturnRef TypeRef
|
||||
ReturnExplicit bool
|
||||
Body []Stmt
|
||||
ExpressionBody Expr
|
||||
InferReturn bool
|
||||
}
|
||||
|
||||
type Param struct {
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var coroutineBuiltins = map[string]bool{"runBlocking": true, "coroutineScope": true, "launch": true, "async": true, "delay": true, "withTimeout": true, "isActive": true, "coroutineContext": true}
|
||||
var coroutineBuiltins = map[string]bool{"runBlocking": true, "withContext": true, "coroutineScope": true, "launch": true, "async": true, "delay": true, "withTimeout": true, "isActive": true, "coroutineContext": true}
|
||||
|
||||
func programUsesCoroutines(program *Program) bool {
|
||||
for _, fn := range program.Functions {
|
||||
if statementsUseCoroutines(fn.Body) {
|
||||
if statementsUseCoroutines(fn.Body) || (fn.ExpressionBody != nil && expressionUsesCoroutines(fn.ExpressionBody)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, class := range program.Classes {
|
||||
for _, fn := range class.Methods {
|
||||
if statementsUseCoroutines(fn.Body) {
|
||||
if statementsUseCoroutines(fn.Body) || (fn.ExpressionBody != nil && expressionUsesCoroutines(fn.ExpressionBody)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -96,7 +96,9 @@ func (g *goGenerator) emitCoroutineSupport() {
|
|||
g.line("func (scope *GotlinCoroutineScope) IsActive() bool { return scope.ctx.Err()==nil }")
|
||||
g.line("func (scope *GotlinCoroutineScope) Context() context.Context { return scope.ctx }")
|
||||
g.line("func (scope *GotlinCoroutineScope) WithTimeout(ms int, block func(*GotlinCoroutineScope)) { ctx,cancel:=context.WithTimeout(scope.ctx,time.Duration(ms)*time.Millisecond); defer cancel(); child:=gotlinNewCoroutineScope(ctx); defer child.cancel(); block(child); child.wait() }")
|
||||
g.line("func (scope *GotlinCoroutineScope) WithContext(ctx context.Context, block func(*GotlinCoroutineScope)) { merged,cancel:=context.WithCancel(ctx); stop:=context.AfterFunc(scope.ctx,cancel); defer stop(); defer cancel(); child:=gotlinNewCoroutineScope(merged); defer child.cancel(); block(child); child.wait() }")
|
||||
g.line("func gotlinRunBlocking(block func(*GotlinCoroutineScope)) { scope:=gotlinNewCoroutineScope(context.Background()); defer scope.cancel(); defer func(){if value:=recover();value!=nil{scope.cancel();scope.workers.Wait();panic(value)}}(); block(scope); scope.wait() }")
|
||||
g.line("func gotlinRunWithContext(ctx context.Context, block func(*GotlinCoroutineScope)) { scope:=gotlinNewCoroutineScope(ctx); defer scope.cancel(); defer func(){if value:=recover();value!=nil{scope.cancel();scope.workers.Wait();panic(value)}}(); block(scope); scope.wait() }")
|
||||
g.line("type GotlinDeferred[T any] struct { done chan struct{}; value T; failure any }")
|
||||
g.line("func gotlinAsync[T any](scope *GotlinCoroutineScope, block func(*GotlinCoroutineScope) T) *GotlinDeferred[T] { deferred:=&GotlinDeferred[T]{done:make(chan struct{})}; scope.Launch(func(child *GotlinCoroutineScope){defer close(deferred.done);defer func(){if value:=recover();value!=nil{deferred.failure=value;scope.fail(value)}}();deferred.value=block(child)}); return deferred }")
|
||||
g.line("func (deferred *GotlinDeferred[T]) await() T { <-deferred.done; if deferred.failure!=nil{panic(deferred.failure)}; return deferred.value }")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ func inferCoroutineEffects(semantic *SemanticProgram) {
|
|||
decl := &semantic.Syntax.Functions[index]
|
||||
symbol, _ := semantic.Global.Lookup(decl.Name)
|
||||
node := &effectNode{key: decl.Name, symbol: symbol}
|
||||
collectFunctionEffects(decl.Body, node)
|
||||
if decl.ExpressionBody != nil {
|
||||
collectExpressionEffects(decl.ExpressionBody, node)
|
||||
} else {
|
||||
collectFunctionEffects(decl.Body, node)
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
for classIndex := range semantic.Syntax.Classes {
|
||||
|
|
@ -31,7 +35,11 @@ func inferCoroutineEffects(semantic *SemanticProgram) {
|
|||
for methodIndex := range class.Methods {
|
||||
method := &class.Methods[methodIndex]
|
||||
node := &effectNode{key: class.Name + "." + method.Name, symbol: classSymbol.Methods[method.Name]}
|
||||
collectFunctionEffects(method.Body, node)
|
||||
if method.ExpressionBody != nil {
|
||||
collectExpressionEffects(method.ExpressionBody, node)
|
||||
} else {
|
||||
collectFunctionEffects(method.Body, node)
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
}
|
||||
|
|
@ -112,6 +120,12 @@ func collectExpressionEffects(expression Expr, node *effectNode) {
|
|||
if ident.Name == "runBlocking" {
|
||||
return
|
||||
}
|
||||
if ident.Name == "withContext" {
|
||||
if len(value.Args) > 0 {
|
||||
collectExpressionEffects(value.Args[0], node)
|
||||
}
|
||||
return
|
||||
}
|
||||
if coroutineBuiltins[ident.Name] {
|
||||
node.direct |= CoroutineEffect
|
||||
}
|
||||
|
|
|
|||
86
internal/lang/expression_function_test.go
Normal file
86
internal/lang/expression_function_test.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package lang
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExpressionBodiedFunctionInfersReturnType(t *testing.T) {
|
||||
program, err := Parse(`package demo
|
||||
fun twice(value: Int) = value * 2
|
||||
fun text() = "value"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output, err := GenerateGo(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"func twice(value int) int", "return value * 2", "func text() string", `return "value"`} {
|
||||
if !strings.Contains(string(output), expected) {
|
||||
t.Fatalf("missing %q:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
if got := ResolvedType(program.Functions[0].ExpressionBody).String(); got != "Int" {
|
||||
t.Fatalf("return type = %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpressionBodyInferenceReachesFixedPoint(t *testing.T) {
|
||||
program, err := Parse(`package demo
|
||||
fun first() = second()
|
||||
fun second() = third()
|
||||
fun third() = 42`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
semantic, err := Analyze(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{"first", "second", "third"} {
|
||||
symbol, _ := semantic.Global.Lookup(name)
|
||||
if got := symbol.Type.(FunctionType).Result.String(); got != "Int" {
|
||||
t.Fatalf("%s result = %s", name, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithContextExpressionBodyEstablishesBoundary(t *testing.T) {
|
||||
program, err := Parse(`package demo
|
||||
import http net.http
|
||||
import exec os.exec
|
||||
fun work() { exec.commandContext("date") }
|
||||
fun handle(request: *http.Request) = withContext(request.context()) {
|
||||
work()
|
||||
}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
semantic, err := Analyze(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !semantic.FunctionEffects["work"].Has(CoroutineEffect) {
|
||||
t.Fatal("work should require ambient context")
|
||||
}
|
||||
if semantic.FunctionEffects["handle"].Has(CoroutineEffect) {
|
||||
t.Fatal("withContext should be an effect boundary")
|
||||
}
|
||||
output, err := GenerateGo(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code := string(output)
|
||||
for _, expected := range []string{
|
||||
"func work(gotlinScope *GotlinCoroutineScope)",
|
||||
`exec.CommandContext(gotlinScope.Context(), "date")`,
|
||||
"func handle(request *http.Request)",
|
||||
"gotlinRunWithContext(request.Context()",
|
||||
"work(gotlinScope)",
|
||||
} {
|
||||
if !strings.Contains(code, expected) {
|
||||
t.Fatalf("missing %q:\n%s", expected, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
g.line("")
|
||||
|
||||
for _, fn := range program.Functions {
|
||||
if usesPrintln(fn.Body) {
|
||||
if usesPrintln(fn.Body) || (fn.ExpressionBody != nil && exprUsesPrintln(fn.ExpressionBody)) {
|
||||
g.needsFmt = true
|
||||
break
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
if !g.needsFmt {
|
||||
for _, class := range program.Classes {
|
||||
for _, method := range class.Methods {
|
||||
if usesPrintln(method.Body) {
|
||||
if usesPrintln(method.Body) || (method.ExpressionBody != nil && exprUsesPrintln(method.ExpressionBody)) {
|
||||
g.needsFmt = true
|
||||
break
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
}
|
||||
if !g.needsRunCatch {
|
||||
for _, fn := range program.Functions {
|
||||
if usesRunCatching(fn.Body) {
|
||||
if usesRunCatching(fn.Body) || (fn.ExpressionBody != nil && exprUsesRunCatching(fn.ExpressionBody)) {
|
||||
g.needsRunCatch = true
|
||||
break
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
}
|
||||
if !g.needsGoUnwrap {
|
||||
for _, fn := range program.Functions {
|
||||
if usesGoUnwrap(fn.Body) {
|
||||
if usesGoUnwrap(fn.Body) || fn.ExpressionBody != nil {
|
||||
g.needsGoUnwrap = true
|
||||
break
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
if !g.needsGoUnwrap {
|
||||
for _, class := range program.Classes {
|
||||
for _, method := range class.Methods {
|
||||
if usesGoUnwrap(method.Body) {
|
||||
if usesGoUnwrap(method.Body) || method.ExpressionBody != nil {
|
||||
g.needsGoUnwrap = true
|
||||
break
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ func (g *goGenerator) program(program *Program, packageOverride string) error {
|
|||
if !g.needsRunCatch {
|
||||
for _, class := range program.Classes {
|
||||
for _, method := range class.Methods {
|
||||
if usesRunCatching(method.Body) {
|
||||
if usesRunCatching(method.Body) || (method.ExpressionBody != nil && exprUsesRunCatching(method.ExpressionBody)) {
|
||||
g.needsRunCatch = true
|
||||
break
|
||||
}
|
||||
|
|
@ -277,13 +277,24 @@ func (g *goGenerator) function(fn FunctionDecl) error {
|
|||
g.write(fn.Name)
|
||||
g.write(renderGoTypeParameters(fn.TypeParams))
|
||||
g.write(g.renderGoFunctionParams(fn))
|
||||
if ret := g.goReturnType(fn.ReturnType); ret != "" {
|
||||
returnType := g.functionReturnType(fn)
|
||||
if ret := g.goReturnType(returnType); ret != "" {
|
||||
g.write(" ")
|
||||
g.write(ret)
|
||||
}
|
||||
g.write(" {\n")
|
||||
g.indentLevel++
|
||||
if err := g.block(fn.Body); err != nil {
|
||||
if fn.ExpressionBody != nil {
|
||||
value, err := g.expr(fn.ExpressionBody, returnType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("function %s: %w", fn.Name, err)
|
||||
}
|
||||
if g.goReturnType(returnType) == "" {
|
||||
g.line(value)
|
||||
} else {
|
||||
g.line("return " + value)
|
||||
}
|
||||
} else if err := g.block(fn.Body); err != nil {
|
||||
return fmt.Errorf("function %s: %w", fn.Name, err)
|
||||
}
|
||||
g.indentLevel--
|
||||
|
|
@ -443,13 +454,24 @@ func (g *goGenerator) method(class ClassDecl, fn FunctionDecl) error {
|
|||
g.write(") ")
|
||||
g.write(fn.Name)
|
||||
g.write(g.renderGoFunctionParams(fn))
|
||||
if ret := g.goReturnType(fn.ReturnType); ret != "" {
|
||||
returnType := g.functionReturnType(fn)
|
||||
if ret := g.goReturnType(returnType); ret != "" {
|
||||
g.write(" ")
|
||||
g.write(ret)
|
||||
}
|
||||
g.write(" {\n")
|
||||
g.indentLevel++
|
||||
if err := g.block(fn.Body); err != nil {
|
||||
if fn.ExpressionBody != nil {
|
||||
value, err := g.expr(fn.ExpressionBody, returnType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("method %s.%s: %w", class.Name, fn.Name, err)
|
||||
}
|
||||
if g.goReturnType(returnType) == "" {
|
||||
g.line(value)
|
||||
} else {
|
||||
g.line("return " + value)
|
||||
}
|
||||
} else if err := g.block(fn.Body); err != nil {
|
||||
return fmt.Errorf("method %s.%s: %w", class.Name, fn.Name, err)
|
||||
}
|
||||
g.indentLevel--
|
||||
|
|
@ -898,6 +920,26 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
return "gotlinRunBlocking(" + block + ")", nil
|
||||
case "withContext":
|
||||
if len(e.Args) != 2 {
|
||||
return "", fmt.Errorf("withContext expects a Go context and a lambda")
|
||||
}
|
||||
ctx, err := g.expr(e.Args[0], "context.Context")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lambda, ok := e.Args[1].(LambdaExpr)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("withContext expects a lambda")
|
||||
}
|
||||
block, err := g.coroutineLambda(lambda, "Unit")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if g.currentCoroutineScope == "" {
|
||||
return "gotlinRunWithContext(" + ctx + ", " + block + ")", nil
|
||||
}
|
||||
return g.currentCoroutineScope + ".WithContext(" + ctx + ", " + block + ")", nil
|
||||
case "coroutineScope", "launch":
|
||||
if g.currentCoroutineScope == "" {
|
||||
return "", fmt.Errorf("%s requires a coroutine scope", ident.Name)
|
||||
|
|
@ -2521,6 +2563,24 @@ func (g *goGenerator) hasCoroutineEffect(function FunctionDecl) bool {
|
|||
return g.semantic.FunctionEffects[key].Has(CoroutineEffect)
|
||||
}
|
||||
|
||||
func (g *goGenerator) functionReturnType(function FunctionDecl) string {
|
||||
if !function.InferReturn {
|
||||
return function.ReturnType
|
||||
}
|
||||
var symbol *Symbol
|
||||
if g.currentClass != nil {
|
||||
symbol = g.semantic.ClassInfo[g.currentClass.Name].Method(function.Name)
|
||||
} else {
|
||||
symbol, _ = g.semantic.Global.Lookup(function.Name)
|
||||
}
|
||||
if symbol != nil {
|
||||
if signature, ok := symbol.Type.(FunctionType); ok {
|
||||
return signature.Result.String()
|
||||
}
|
||||
}
|
||||
return function.ReturnType
|
||||
}
|
||||
|
||||
func renderGoTypeParameters(params []string) string {
|
||||
if len(params) == 0 {
|
||||
return ""
|
||||
|
|
@ -2855,6 +2915,9 @@ func usedImportAliases(program *Program) map[string]bool {
|
|||
markType(param.Type)
|
||||
}
|
||||
markType(method.ReturnType)
|
||||
if method.ExpressionBody != nil {
|
||||
walkExpr(method.ExpressionBody)
|
||||
}
|
||||
for _, stmt := range method.Body {
|
||||
walkStmt(stmt)
|
||||
}
|
||||
|
|
@ -2865,6 +2928,9 @@ func usedImportAliases(program *Program) map[string]bool {
|
|||
markType(param.Type)
|
||||
}
|
||||
markType(fn.ReturnType)
|
||||
if fn.ExpressionBody != nil {
|
||||
walkExpr(fn.ExpressionBody)
|
||||
}
|
||||
for _, stmt := range fn.Body {
|
||||
walkStmt(stmt)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,9 +109,11 @@ type HIRMapping struct {
|
|||
func (HIRMapping) hirNode() {}
|
||||
|
||||
type HIRFunction struct {
|
||||
Symbol *Symbol
|
||||
Decl *FunctionDecl
|
||||
Scope *Scope
|
||||
Symbol *Symbol
|
||||
Decl *FunctionDecl
|
||||
Scope *Scope
|
||||
Expression *HIRExpr
|
||||
Result Type
|
||||
}
|
||||
|
||||
type HIRProgram struct {
|
||||
|
|
@ -249,8 +251,19 @@ func (resolver *semanticResolver) resolve() error {
|
|||
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
result, _ := resolver.program.ResolveTypeRefWithParams(decl.ReturnRef, decl.TypeParams)
|
||||
resolver.resolveStmts(decl.Body, scope, nil, result)
|
||||
resolver.program.HIR.Functions = append(resolver.program.HIR.Functions, &HIRFunction{Symbol: symbol, Decl: decl, Scope: scope})
|
||||
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]
|
||||
|
|
@ -266,13 +279,69 @@ func (resolver *semanticResolver) resolve() error {
|
|||
}
|
||||
params := append(append([]string{}, decl.TypeParams...), method.TypeParams...)
|
||||
result, _ := resolver.program.ResolveTypeRefWithParams(method.ReturnRef, params)
|
||||
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})
|
||||
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) {
|
||||
|
|
@ -768,7 +837,7 @@ var semanticBuiltins = map[string]bool{
|
|||
"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, "coroutineScope": true, "launch": true, "async": true,
|
||||
"runBlocking": true, "withContext": true, "coroutineScope": true, "launch": true, "async": true,
|
||||
"delay": true, "withTimeout": true, "isActive": true, "coroutineContext": true,
|
||||
"continue": true, "break": true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,13 @@ func BuildPackageMetadata(program *Program, importPath string) (*PackageMetadata
|
|||
}
|
||||
for methodIndex := range class.Methods {
|
||||
method := &class.Methods[methodIndex]
|
||||
item.Methods = append(item.Methods, metadataFunction(method, semantic.FunctionEffects[class.Name+"."+method.Name]))
|
||||
result := Type(nil)
|
||||
if symbol := semantic.ClassInfo[class.Name].Method(method.Name); symbol != nil {
|
||||
if signature, ok := symbol.Type.(FunctionType); ok {
|
||||
result = signature.Result
|
||||
}
|
||||
}
|
||||
item.Methods = append(item.Methods, metadataFunction(method, semantic.FunctionEffects[class.Name+"."+method.Name], result))
|
||||
}
|
||||
metadata.Classes = append(metadata.Classes, item)
|
||||
}
|
||||
|
|
@ -78,13 +84,23 @@ func BuildPackageMetadata(program *Program, importPath string) (*PackageMetadata
|
|||
}
|
||||
for index := range program.Functions {
|
||||
function := &program.Functions[index]
|
||||
metadata.Functions = append(metadata.Functions, metadataFunction(function, semantic.FunctionEffects[function.Name]))
|
||||
result := Type(nil)
|
||||
if symbol, ok := semantic.Global.Lookup(function.Name); ok {
|
||||
if signature, ok := symbol.Type.(FunctionType); ok {
|
||||
result = signature.Result
|
||||
}
|
||||
}
|
||||
metadata.Functions = append(metadata.Functions, metadataFunction(function, semantic.FunctionEffects[function.Name], result))
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func metadataFunction(function *FunctionDecl, effects Effect) FunctionMetadata {
|
||||
item := FunctionMetadata{Name: function.Name, TypeParams: function.TypeParams, Result: function.ReturnType, Effects: effects}
|
||||
func metadataFunction(function *FunctionDecl, effects Effect, result Type) FunctionMetadata {
|
||||
resultName := function.ReturnType
|
||||
if result != nil && !isUnknownType(result) {
|
||||
resultName = result.String()
|
||||
}
|
||||
item := FunctionMetadata{Name: function.Name, TypeParams: function.TypeParams, Result: resultName, Effects: effects}
|
||||
for _, param := range function.Params {
|
||||
item.Params = append(item.Params, ParamMetadata{Name: param.Name, Type: param.Type})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,18 +471,28 @@ func (p *parser) parseFunction() (FunctionDecl, error) {
|
|||
if err != nil {
|
||||
return FunctionDecl{}, err
|
||||
}
|
||||
function := FunctionDecl{
|
||||
Name: signature.Name,
|
||||
TypeParams: signature.TypeParams,
|
||||
Params: signature.Params,
|
||||
ReturnType: signature.ReturnType,
|
||||
ReturnExplicit: signature.ReturnExplicit,
|
||||
}
|
||||
if p.match(tokenAssign) {
|
||||
expression, err := p.parseExpr(0)
|
||||
if err != nil {
|
||||
return FunctionDecl{}, err
|
||||
}
|
||||
function.ExpressionBody = expression
|
||||
function.InferReturn = !signature.ReturnExplicit
|
||||
return function, nil
|
||||
}
|
||||
body, err := p.parseBlock()
|
||||
if err != nil {
|
||||
return FunctionDecl{}, err
|
||||
}
|
||||
|
||||
return FunctionDecl{
|
||||
Name: signature.Name,
|
||||
TypeParams: signature.TypeParams,
|
||||
Params: signature.Params,
|
||||
ReturnType: signature.ReturnType,
|
||||
Body: body,
|
||||
}, nil
|
||||
function.Body = body
|
||||
return function, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
|
||||
|
|
@ -509,7 +519,9 @@ func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
|
|||
}
|
||||
|
||||
returnType := "Unit"
|
||||
returnExplicit := false
|
||||
if p.match(tokenColon) {
|
||||
returnExplicit = true
|
||||
typ, err := p.parseTypeRef()
|
||||
if err != nil {
|
||||
return FunctionSignature{}, err
|
||||
|
|
@ -518,10 +530,11 @@ func (p *parser) parseFunctionSignature() (FunctionSignature, error) {
|
|||
}
|
||||
|
||||
return FunctionSignature{
|
||||
Name: name.lexeme,
|
||||
TypeParams: typeParams,
|
||||
Params: params,
|
||||
ReturnType: returnType,
|
||||
Name: name.lexeme,
|
||||
TypeParams: typeParams,
|
||||
Params: params,
|
||||
ReturnType: returnType,
|
||||
ReturnExplicit: returnExplicit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ func (checker *mutabilityChecker) checkFunction(function *FunctionDecl, class *C
|
|||
typ, _ := checker.semantic.ResolveTypeRefWithParams(param.TypeRef, typeParams)
|
||||
_ = checker.scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
if function.ExpressionBody != nil {
|
||||
return checker.checkExpr(function.ExpressionBody)
|
||||
}
|
||||
return checker.checkStmts(function.Body)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1221,12 +1221,18 @@ func stmtsMatch(stmts []Stmt, match func(Expr) bool) bool {
|
|||
|
||||
func programExprMatches(program *Program, match func(Expr) bool) bool {
|
||||
for _, fn := range program.Functions {
|
||||
if fn.ExpressionBody != nil && exprMatches(fn.ExpressionBody, match) {
|
||||
return true
|
||||
}
|
||||
if stmtsMatch(fn.Body, match) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, class := range program.Classes {
|
||||
for _, fn := range class.Methods {
|
||||
if fn.ExpressionBody != nil && exprMatches(fn.ExpressionBody, match) {
|
||||
return true
|
||||
}
|
||||
if stmtsMatch(fn.Body, match) {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,11 @@ func analyzeProgram(program *Program, additional []*Program, metadata []*Package
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("function %s: %w", decl.Name, err)
|
||||
}
|
||||
if decl.InferReturn {
|
||||
function := typ.(FunctionType)
|
||||
function.Result = UnknownType{}
|
||||
typ = function
|
||||
}
|
||||
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: FunctionSymbolKind, Type: typ, Decl: decl}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -215,6 +220,11 @@ func analyzeProgram(program *Program, additional []*Program, metadata []*Package
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("method %s.%s: %w", decl.Name, method.Name, err)
|
||||
}
|
||||
if method.InferReturn {
|
||||
function := typ.(FunctionType)
|
||||
function.Result = UnknownType{}
|
||||
typ = function
|
||||
}
|
||||
class.Methods[method.Name] = &Symbol{Name: method.Name, Kind: FunctionSymbolKind, Type: typ, Decl: method}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ func (semantic *SemanticProgram) TypeOf(expr Expr, environment TypeEnvironment)
|
|||
}
|
||||
case "coroutineContext":
|
||||
return NamedType{Name: "context.Context"}
|
||||
case "withContext":
|
||||
return NamedType{Name: "Unit"}
|
||||
}
|
||||
if ident.Name == "keys" && len(value.Args) == 1 {
|
||||
if mapping, ok := semantic.TypeOf(value.Args[0], environment).(GenericType); ok && (mapping.Base.String() == "Map" || mapping.Base.String() == "MutableMap") && len(mapping.Args) == 2 {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ func hydrateFunctionRefs(function *FunctionDecl) error {
|
|||
return err
|
||||
}
|
||||
function.ReturnRef = ref
|
||||
if function.ExpressionBody != nil {
|
||||
if err := hydrateExprTypeRefs(function.ExpressionBody); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return hydrateStmtTypeRefs(function.Body)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue