// RETURNS: // - type expression which represents a full name of a type // - bool whether a type expression is actually a type (used internally) // - scope in which type makes sense func infer_type(v ast.Expr, scope *scope, index int) (ast.Expr, *scope, bool) { switch t := v.(type) { case *ast.CompositeLit: return t.Type, scope, true case *ast.Ident: if d := scope.lookup(t.Name); d != nil { if d.class == decl_package { return ast.NewIdent(t.Name), scope, false } typ, scope := d.infer_type() return typ, scope, d.class == decl_type } case *ast.UnaryExpr: switch t.Op { case token.AND: // &a makes sense only with values, don't even check for type it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } e := new(ast.StarExpr) e.X = it return e, s, false case token.ARROW: // <-a makes sense only with values it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } switch index { case -1, 0: it, s = advance_to_type(chan_predicate, it, s) return it.(*ast.ChanType).Value, s, false case 1: // technically it's a value, but in case of index == 1 // it is always the last infer operation return ast.NewIdent("bool"), g_universe_scope, false } case token.ADD, token.NOT, token.SUB, token.XOR: it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } return it, s, false } case *ast.BinaryExpr: switch t.Op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LOR, token.LAND: // logic operations, the result is a bool, always return ast.NewIdent("bool"), g_universe_scope, false case token.ADD, token.SUB, token.MUL, token.QUO, token.OR, token.XOR, token.REM, token.AND, token.AND_NOT: // try X, then Y, they should be the same anyway it, s, _ := infer_type(t.X, scope, -1) if it == nil { it, s, _ = infer_type(t.Y, scope, -1) if it == nil { break } } return it, s, false case token.SHL, token.SHR: // try only X for shifts, Y is always uint it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } return it, s, false } case *ast.IndexExpr: // something[another] always returns a value and it works on a value too it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } it, s = advance_to_type(index_predicate, it, s) switch t := it.(type) { case *ast.ArrayType: return t.Elt, s, false case *ast.Ellipsis: return t.Elt, s, false case *ast.MapType: switch index { case -1, 0: return t.Value, s, false case 1: return ast.NewIdent("bool"), g_universe_scope, false } } case *ast.SliceExpr: // something[start : end] always returns a value it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } it, s = advance_to_type(index_predicate, it, s) switch t := it.(type) { case *ast.ArrayType: e := new(ast.ArrayType) e.Elt = t.Elt return e, s, false } case *ast.StarExpr: it, s, is_type := infer_type(t.X, scope, -1) if it == nil { break } if is_type { // if it's a type, add * modifier, make it a 'pointer of' type e := new(ast.StarExpr) e.X = it return e, s, true } else { it, s := advance_to_type(star_predicate, it, s) if se, ok := it.(*ast.StarExpr); ok { return se.X, s, false } } case *ast.CallExpr: // this is a function call or a type cast: // myFunc(1,2,3) or int16(myvar) it, s, is_type := infer_type(t.Fun, scope, -1) if it == nil { break } if is_type { // a type cast return it, scope, false } else { // it must be a function call or a built-in function // first check for built-in if ct, ok := it.(*ast.Ident); ok { ty, s := check_for_builtin_funcs(ct, t, scope) if ty != nil { return ty, s, false } } // then check for an ordinary function call it, scope = advance_to_type(func_predicate, it, s) if ct, ok := it.(*ast.FuncType); ok { return func_return_type(ct, index), s, false } } case *ast.ParenExpr: it, s, is_type := infer_type(t.X, scope, -1) if it == nil { break } return it, s, is_type case *ast.SelectorExpr: it, s, _ := infer_type(t.X, scope, -1) if it == nil { break } if d := type_to_decl(it, s); d != nil { c := d.find_child_and_in_embedded(t.Sel.Name) if c != nil { if c.class == decl_type { return t, scope, true } else { typ, s := c.infer_type() return typ, s, false } } } case *ast.FuncLit: // it's a value, but I think most likely we don't even care, cause we can only // call it, and CallExpr uses the type itself to figure out return t.Type, scope, false case *ast.TypeAssertExpr: if t.Type == nil { return infer_type(t.X, scope, -1) } switch index { case -1, 0: // converting a value to a different type, but return thing is a value it, _, _ := infer_type(t.Type, scope, -1) return it, scope, false case 1: return ast.NewIdent("bool"), g_universe_scope, false } case *ast.ArrayType, *ast.MapType, *ast.ChanType, *ast.Ellipsis, *ast.FuncType, *ast.StructType, *ast.InterfaceType: return t, scope, true default: _ = reflect.TypeOf(v) //fmt.Println(ty) } return nil, nil, false }