175 lines
5 KiB
Go
175 lines
5 KiB
Go
package lang
|
|
|
|
type Effect uint8
|
|
|
|
const (
|
|
NoEffect Effect = 0
|
|
CoroutineEffect Effect = 1 << 0
|
|
)
|
|
|
|
func (effect Effect) Has(required Effect) bool { return effect&required != 0 }
|
|
|
|
type effectNode struct {
|
|
key string
|
|
symbol *Symbol
|
|
direct Effect
|
|
callees []*Symbol
|
|
}
|
|
|
|
func inferCoroutineEffects(semantic *SemanticProgram) {
|
|
var nodes []*effectNode
|
|
for index := range semantic.Syntax.Functions {
|
|
decl := &semantic.Syntax.Functions[index]
|
|
symbol, _ := semantic.Global.Lookup(decl.Name)
|
|
node := &effectNode{key: decl.Name, symbol: symbol}
|
|
if decl.ExpressionBody != nil {
|
|
collectExpressionEffects(decl.ExpressionBody, node)
|
|
} else {
|
|
collectFunctionEffects(decl.Body, node)
|
|
}
|
|
nodes = append(nodes, node)
|
|
}
|
|
for classIndex := range semantic.Syntax.Classes {
|
|
class := &semantic.Syntax.Classes[classIndex]
|
|
classSymbol := semantic.ClassInfo[class.Name]
|
|
for methodIndex := range class.Methods {
|
|
method := &class.Methods[methodIndex]
|
|
node := &effectNode{key: class.Name + "." + method.Name, symbol: classSymbol.Methods[method.Name]}
|
|
if method.ExpressionBody != nil {
|
|
collectExpressionEffects(method.ExpressionBody, node)
|
|
} else {
|
|
collectFunctionEffects(method.Body, node)
|
|
}
|
|
nodes = append(nodes, node)
|
|
}
|
|
}
|
|
changed := true
|
|
for changed {
|
|
changed = false
|
|
for _, node := range nodes {
|
|
effects := node.direct
|
|
for _, callee := range node.callees {
|
|
effects |= callee.Effects
|
|
}
|
|
if node.symbol != nil && node.symbol.Effects != effects {
|
|
node.symbol.Effects = effects
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
for _, node := range nodes {
|
|
if node.symbol != nil {
|
|
semantic.FunctionEffects[node.key] = node.symbol.Effects
|
|
if function, ok := node.symbol.Type.(FunctionType); ok {
|
|
function.Effects = node.symbol.Effects
|
|
node.symbol.Type = function
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func collectFunctionEffects(statements []Stmt, node *effectNode) {
|
|
for _, statement := range statements {
|
|
switch value := statement.(type) {
|
|
case VarDecl:
|
|
collectExpressionEffects(value.Value, node)
|
|
case MultiVarDecl:
|
|
collectExpressionEffects(value.Value, node)
|
|
case AssignStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case AddAssignStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case MultiAssignStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case FieldAssignStmt:
|
|
collectExpressionEffects(value.Target, node)
|
|
collectExpressionEffects(value.Value, node)
|
|
case ReturnStmt:
|
|
if value.Value != nil {
|
|
collectExpressionEffects(value.Value, node)
|
|
}
|
|
case ThrowStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case DeferStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case ExprStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
case IfStmt:
|
|
collectExpressionEffects(value.Cond, node)
|
|
collectFunctionEffects(value.Then, node)
|
|
collectFunctionEffects(value.Else, node)
|
|
case WhileStmt:
|
|
collectExpressionEffects(value.Cond, node)
|
|
collectFunctionEffects(value.Body, node)
|
|
case ForEachStmt:
|
|
collectExpressionEffects(value.Source, node)
|
|
collectFunctionEffects(value.Body, node)
|
|
case MatchStmt:
|
|
collectExpressionEffects(value.Value, node)
|
|
for _, matchCase := range value.Cases {
|
|
collectFunctionEffects(matchCase.Body, node)
|
|
}
|
|
case TryCatchStmt:
|
|
collectFunctionEffects(value.TryBody, node)
|
|
collectFunctionEffects(value.CatchBody, node)
|
|
}
|
|
}
|
|
}
|
|
|
|
func collectExpressionEffects(expression Expr, node *effectNode) {
|
|
switch value := expression.(type) {
|
|
case CallExpr:
|
|
if ident, ok := value.Callee.(IdentExpr); ok {
|
|
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
|
|
}
|
|
}
|
|
if semantic := exprMeta(value); semantic != nil {
|
|
if call, ok := semantic.Node.(HIRGotlinCall); ok && call.Target != nil {
|
|
node.callees = append(node.callees, call.Target)
|
|
}
|
|
if call, ok := semantic.Node.(HIRGoCall); ok && call.InjectContext {
|
|
node.direct |= CoroutineEffect
|
|
}
|
|
}
|
|
collectExpressionEffects(value.Callee, node)
|
|
for _, argument := range value.Args {
|
|
collectExpressionEffects(argument, node)
|
|
}
|
|
for _, argument := range value.NamedArgs {
|
|
collectExpressionEffects(argument.Value, node)
|
|
}
|
|
case UnaryExpr:
|
|
collectExpressionEffects(value.Value, node)
|
|
case NonNullExpr:
|
|
collectExpressionEffects(value.Value, node)
|
|
case TryExpr:
|
|
collectExpressionEffects(value.Value, node)
|
|
case BinaryExpr:
|
|
collectExpressionEffects(value.Left, node)
|
|
collectExpressionEffects(value.Right, node)
|
|
case SelectorExpr:
|
|
collectExpressionEffects(value.Receiver, node)
|
|
case SafeSelectorExpr:
|
|
collectExpressionEffects(value.Receiver, node)
|
|
case IndexExpr:
|
|
collectExpressionEffects(value.Receiver, node)
|
|
collectExpressionEffects(value.Index, node)
|
|
case MatchExpr:
|
|
collectExpressionEffects(value.Value, node)
|
|
for _, matchCase := range value.Cases {
|
|
collectExpressionEffects(matchCase.Value, node)
|
|
}
|
|
case LambdaExpr:
|
|
collectFunctionEffects(value.Body, node)
|
|
}
|
|
}
|