github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/config/lang/check_types.go (about) 1 package lang 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/hashicorp/terraform/config/lang/ast" 8 ) 9 10 // TypeCheck implements ast.Visitor for type checking an AST tree. 11 // It requires some configuration to look up the type of nodes. 12 // 13 // It also optionally will not type error and will insert an implicit 14 // type conversions for specific types if specified by the Implicit 15 // field. Note that this is kind of organizationally weird to put into 16 // this structure but we'd rather do that than duplicate the type checking 17 // logic multiple times. 18 type TypeCheck struct { 19 Scope ast.Scope 20 21 // Implicit is a map of implicit type conversions that we can do, 22 // and that shouldn't error. The key of the first map is the from type, 23 // the key of the second map is the to type, and the final string 24 // value is the function to call (which must be registered in the Scope). 25 Implicit map[ast.Type]map[ast.Type]string 26 27 // Stack of types. This shouldn't be used directly except by implementations 28 // of TypeCheckNode. 29 Stack []ast.Type 30 31 err error 32 lock sync.Mutex 33 } 34 35 // TypeCheckNode is the interface that must be implemented by any 36 // ast.Node that wants to support type-checking. If the type checker 37 // encounters a node that doesn't implement this, it will error. 38 type TypeCheckNode interface { 39 TypeCheck(*TypeCheck) (ast.Node, error) 40 } 41 42 func (v *TypeCheck) Visit(root ast.Node) error { 43 v.lock.Lock() 44 defer v.lock.Unlock() 45 defer v.reset() 46 root.Accept(v.visit) 47 return v.err 48 } 49 50 func (v *TypeCheck) visit(raw ast.Node) ast.Node { 51 if v.err != nil { 52 return raw 53 } 54 55 var result ast.Node 56 var err error 57 switch n := raw.(type) { 58 case *ast.Arithmetic: 59 tc := &typeCheckArithmetic{n} 60 result, err = tc.TypeCheck(v) 61 case *ast.Call: 62 tc := &typeCheckCall{n} 63 result, err = tc.TypeCheck(v) 64 case *ast.Concat: 65 tc := &typeCheckConcat{n} 66 result, err = tc.TypeCheck(v) 67 case *ast.LiteralNode: 68 tc := &typeCheckLiteral{n} 69 result, err = tc.TypeCheck(v) 70 case *ast.VariableAccess: 71 tc := &typeCheckVariableAccess{n} 72 result, err = tc.TypeCheck(v) 73 default: 74 tc, ok := raw.(TypeCheckNode) 75 if !ok { 76 err = fmt.Errorf("unknown node for type check: %#v", raw) 77 break 78 } 79 80 result, err = tc.TypeCheck(v) 81 } 82 83 if err != nil { 84 pos := raw.Pos() 85 v.err = fmt.Errorf("At column %d, line %d: %s", 86 pos.Column, pos.Line, err) 87 } 88 89 return result 90 } 91 92 type typeCheckArithmetic struct { 93 n *ast.Arithmetic 94 } 95 96 func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { 97 // The arguments are on the stack in reverse order, so pop them off. 98 exprs := make([]ast.Type, len(tc.n.Exprs)) 99 for i, _ := range tc.n.Exprs { 100 exprs[len(tc.n.Exprs)-1-i] = v.StackPop() 101 } 102 103 // Determine the resulting type we want. We do this by going over 104 // every expression until we find one with a type we recognize. 105 // We do this because the first expr might be a string ("var.foo") 106 // and we need to know what to implicit to. 107 mathFunc := "__builtin_IntMath" 108 mathType := ast.TypeInt 109 for _, v := range exprs { 110 exit := true 111 switch v { 112 case ast.TypeInt: 113 mathFunc = "__builtin_IntMath" 114 mathType = v 115 case ast.TypeFloat: 116 mathFunc = "__builtin_FloatMath" 117 mathType = v 118 default: 119 exit = false 120 } 121 122 // We found the type, so leave 123 if exit { 124 break 125 } 126 } 127 128 // Verify the args 129 for i, arg := range exprs { 130 if arg != mathType { 131 cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i]) 132 if cn != nil { 133 tc.n.Exprs[i] = cn 134 continue 135 } 136 137 return nil, fmt.Errorf( 138 "operand %d should be %s, got %s", 139 i+1, mathType, arg) 140 } 141 } 142 143 // Modulo doesn't work for floats 144 if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod { 145 return nil, fmt.Errorf("modulo cannot be used with floats") 146 } 147 148 // Return type 149 v.StackPush(mathType) 150 151 // Replace our node with a call to the proper function. This isn't 152 // type checked but we already verified types. 153 args := make([]ast.Node, len(tc.n.Exprs)+1) 154 args[0] = &ast.LiteralNode{ 155 Value: tc.n.Op, 156 Typex: ast.TypeInt, 157 Posx: tc.n.Pos(), 158 } 159 copy(args[1:], tc.n.Exprs) 160 return &ast.Call{ 161 Func: mathFunc, 162 Args: args, 163 Posx: tc.n.Pos(), 164 }, nil 165 } 166 167 type typeCheckCall struct { 168 n *ast.Call 169 } 170 171 func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { 172 // Look up the function in the map 173 function, ok := v.Scope.LookupFunc(tc.n.Func) 174 if !ok { 175 return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) 176 } 177 178 // The arguments are on the stack in reverse order, so pop them off. 179 args := make([]ast.Type, len(tc.n.Args)) 180 for i, _ := range tc.n.Args { 181 args[len(tc.n.Args)-1-i] = v.StackPop() 182 } 183 184 // Verify the args 185 for i, expected := range function.ArgTypes { 186 if expected == ast.TypeAny { 187 continue 188 } 189 190 if args[i] != expected { 191 cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) 192 if cn != nil { 193 tc.n.Args[i] = cn 194 continue 195 } 196 197 return nil, fmt.Errorf( 198 "%s: argument %d should be %s, got %s", 199 tc.n.Func, i+1, expected, args[i]) 200 } 201 } 202 203 // If we're variadic, then verify the types there 204 if function.Variadic && function.VariadicType != ast.TypeAny { 205 args = args[len(function.ArgTypes):] 206 for i, t := range args { 207 if t != function.VariadicType { 208 realI := i + len(function.ArgTypes) 209 cn := v.ImplicitConversion( 210 t, function.VariadicType, tc.n.Args[realI]) 211 if cn != nil { 212 tc.n.Args[realI] = cn 213 continue 214 } 215 216 return nil, fmt.Errorf( 217 "%s: argument %d should be %s, got %s", 218 tc.n.Func, realI, 219 function.VariadicType, t) 220 } 221 } 222 } 223 224 // Return type 225 v.StackPush(function.ReturnType) 226 227 return tc.n, nil 228 } 229 230 type typeCheckConcat struct { 231 n *ast.Concat 232 } 233 234 func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) { 235 n := tc.n 236 types := make([]ast.Type, len(n.Exprs)) 237 for i, _ := range n.Exprs { 238 types[len(n.Exprs)-1-i] = v.StackPop() 239 } 240 241 // All concat args must be strings, so validate that 242 for i, t := range types { 243 if t != ast.TypeString { 244 cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) 245 if cn != nil { 246 n.Exprs[i] = cn 247 continue 248 } 249 250 return nil, fmt.Errorf( 251 "argument %d must be a string", i+1) 252 } 253 } 254 255 // This always results in type string 256 v.StackPush(ast.TypeString) 257 258 return n, nil 259 } 260 261 type typeCheckLiteral struct { 262 n *ast.LiteralNode 263 } 264 265 func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { 266 v.StackPush(tc.n.Typex) 267 return tc.n, nil 268 } 269 270 type typeCheckVariableAccess struct { 271 n *ast.VariableAccess 272 } 273 274 func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { 275 // Look up the variable in the map 276 variable, ok := v.Scope.LookupVar(tc.n.Name) 277 if !ok { 278 return nil, fmt.Errorf( 279 "unknown variable accessed: %s", tc.n.Name) 280 } 281 282 // Add the type to the stack 283 v.StackPush(variable.Type) 284 285 return tc.n, nil 286 } 287 288 func (v *TypeCheck) ImplicitConversion( 289 actual ast.Type, expected ast.Type, n ast.Node) ast.Node { 290 if v.Implicit == nil { 291 return nil 292 } 293 294 fromMap, ok := v.Implicit[actual] 295 if !ok { 296 return nil 297 } 298 299 toFunc, ok := fromMap[expected] 300 if !ok { 301 return nil 302 } 303 304 return &ast.Call{ 305 Func: toFunc, 306 Args: []ast.Node{n}, 307 Posx: n.Pos(), 308 } 309 } 310 311 func (v *TypeCheck) reset() { 312 v.Stack = nil 313 v.err = nil 314 } 315 316 func (v *TypeCheck) StackPush(t ast.Type) { 317 v.Stack = append(v.Stack, t) 318 } 319 320 func (v *TypeCheck) StackPop() ast.Type { 321 var x ast.Type 322 x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] 323 return x 324 }