Infer coroutine effects automatically
This commit is contained in:
parent
bac1183593
commit
5b30e49486
15 changed files with 339 additions and 103 deletions
|
|
@ -251,7 +251,11 @@ func (g *goGenerator) function(fn FunctionDecl) error {
|
|||
g.currentClass = nil
|
||||
g.currentFunc = fn
|
||||
previousScope := g.currentCoroutineScope
|
||||
if fn.Suspend {
|
||||
hasCoroutineEffect := g.hasCoroutineEffect(fn)
|
||||
if hasCoroutineEffect && fn.Name == "main" {
|
||||
return fmt.Errorf("contextual function main requires a runBlocking boundary")
|
||||
}
|
||||
if hasCoroutineEffect {
|
||||
g.currentCoroutineScope = "gotlinScope"
|
||||
}
|
||||
defer func() { g.currentCoroutineScope = previousScope }()
|
||||
|
|
@ -284,11 +288,7 @@ func (g *goGenerator) interfaceDecl(decl InterfaceDecl) {
|
|||
g.line("type " + decl.Name + " interface {")
|
||||
g.indentLevel++
|
||||
for _, method := range decl.Methods {
|
||||
prefix := ""
|
||||
if method.Suspend {
|
||||
prefix = "gotlinScope *GotlinCoroutineScope"
|
||||
}
|
||||
g.line(method.Name + g.renderGoParamsWithPrefix(method.Params, prefix) + g.renderGoReturnSuffix(method.ReturnType))
|
||||
g.line(method.Name + g.renderGoParamsWithPrefix(method.Params, "") + g.renderGoReturnSuffix(method.ReturnType))
|
||||
}
|
||||
g.indentLevel--
|
||||
g.line("}")
|
||||
|
|
@ -416,7 +416,7 @@ func (g *goGenerator) method(class ClassDecl, fn FunctionDecl) error {
|
|||
g.currentClass = &class
|
||||
g.currentFunc = fn
|
||||
previousScope := g.currentCoroutineScope
|
||||
if fn.Suspend {
|
||||
if g.hasCoroutineEffect(fn) {
|
||||
g.currentCoroutineScope = "gotlinScope"
|
||||
}
|
||||
defer func() { g.currentCoroutineScope = previousScope }()
|
||||
|
|
@ -954,6 +954,11 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
|||
return "", fmt.Errorf("isActive requires a coroutine scope")
|
||||
}
|
||||
return g.currentCoroutineScope + ".IsActive()", nil
|
||||
case "coroutineContext":
|
||||
if g.currentCoroutineScope == "" {
|
||||
return "", fmt.Errorf("coroutineContext requires a coroutine scope")
|
||||
}
|
||||
return g.currentCoroutineScope + ".Context()", nil
|
||||
}
|
||||
}
|
||||
if selector, ok := e.Callee.(SelectorExpr); ok && selector.Name == "await" && len(e.Args) == 0 {
|
||||
|
|
@ -1197,27 +1202,14 @@ func (g *goGenerator) expr(expr Expr, expectedType string) (string, error) {
|
|||
return fmt.Sprintf("New%s%s(%s)", ident.Name, g.renderCallTypeArguments(e.TypeArgs), strings.Join(args, ", ")), nil
|
||||
}
|
||||
}
|
||||
if ident, ok := e.Callee.(IdentExpr); ok {
|
||||
if fn, found := g.semantic.Functions[ident.Name]; found && fn.Suspend {
|
||||
if resolvedCall != nil {
|
||||
if call, ok := resolvedCall.Node.(HIRGotlinCall); ok && call.Target != nil && call.Target.Effects.Has(CoroutineEffect) {
|
||||
if g.currentCoroutineScope == "" {
|
||||
return "", fmt.Errorf("suspend function %s requires a coroutine scope", ident.Name)
|
||||
return "", fmt.Errorf("contextual function %s requires a coroutine scope", call.Target.Name)
|
||||
}
|
||||
args = append([]string{g.currentCoroutineScope}, args...)
|
||||
}
|
||||
}
|
||||
if selector, ok := e.Callee.(SelectorExpr); ok {
|
||||
if class, found := g.classForType(g.exprType(selector.Receiver)); found {
|
||||
for _, method := range class.Methods {
|
||||
if method.Name == selector.Name && method.Suspend {
|
||||
if g.currentCoroutineScope == "" {
|
||||
return "", fmt.Errorf("suspend method %s requires a coroutine scope", selector.Name)
|
||||
}
|
||||
args = append([]string{g.currentCoroutineScope}, args...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callee, err := g.expr(e.Callee, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -2491,12 +2483,20 @@ func (g *goGenerator) cloneScopes() []map[string]bool {
|
|||
|
||||
func (g *goGenerator) renderGoFunctionParams(function FunctionDecl) string {
|
||||
prefix := ""
|
||||
if function.Suspend {
|
||||
if g.hasCoroutineEffect(function) {
|
||||
prefix = "gotlinScope *GotlinCoroutineScope"
|
||||
}
|
||||
return g.renderGoParamsWithPrefix(function.Params, prefix)
|
||||
}
|
||||
|
||||
func (g *goGenerator) hasCoroutineEffect(function FunctionDecl) bool {
|
||||
key := function.Name
|
||||
if g.currentClass != nil {
|
||||
key = g.currentClass.Name + "." + function.Name
|
||||
}
|
||||
return g.semantic.FunctionEffects[key].Has(CoroutineEffect)
|
||||
}
|
||||
|
||||
func renderGoTypeParameters(params []string) string {
|
||||
if len(params) == 0 {
|
||||
return ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue