github.com/leeprovoost/terraform@v0.6.10-0.20160119085442-96f3f76118e7/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.UnaryArithmetic: 59 tc := &typeCheckUnaryArithmetic{n} 60 result, err = tc.TypeCheck(v) 61 case *ast.Arithmetic: 62 tc := &typeCheckArithmetic{n} 63 result, err = tc.TypeCheck(v) 64 case *ast.Call: 65 tc := &typeCheckCall{n} 66 result, err = tc.TypeCheck(v) 67 case *ast.Concat: 68 tc := &typeCheckConcat{n} 69 result, err = tc.TypeCheck(v) 70 case *ast.LiteralNode: 71 tc := &typeCheckLiteral{n} 72 result, err = tc.TypeCheck(v) 73 case *ast.VariableAccess: 74 tc := &typeCheckVariableAccess{n} 75 result, err = tc.TypeCheck(v) 76 default: 77 tc, ok := raw.(TypeCheckNode) 78 if !ok { 79 err = fmt.Errorf("unknown node for type check: %#v", raw) 80 break 81 } 82 83 result, err = tc.TypeCheck(v) 84 } 85 86 if err != nil { 87 pos := raw.Pos() 88 v.err = fmt.Errorf("At column %d, line %d: %s", 89 pos.Column, pos.Line, err) 90 } 91 92 return result 93 } 94 95 type typeCheckUnaryArithmetic struct { 96 n *ast.UnaryArithmetic 97 } 98 99 func (tc *typeCheckUnaryArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { 100 // Only support + or - as unary op 101 if tc.n.Op != ast.ArithmeticOpAdd && tc.n.Op != ast.ArithmeticOpSub { 102 fmt.Printf("%+v\n", tc.n.Op) 103 return nil, fmt.Errorf("only + or - supported as unary operator") 104 } 105 expr := v.StackPop() 106 107 mathFunc := "__builtin_UnaryIntMath" 108 mathType := ast.TypeInt 109 switch expr { 110 case ast.TypeInt: 111 mathFunc = "__builtin_UnaryIntMath" 112 mathType = expr 113 case ast.TypeFloat: 114 mathFunc = "__builtin_UnaryFloatMath" 115 mathType = expr 116 } 117 118 // Return type 119 v.StackPush(mathType) 120 121 args := make([]ast.Node, 2) 122 args[0] = &ast.LiteralNode{ 123 Value: tc.n.Op, 124 Typex: ast.TypeInt, 125 Posx: tc.n.Pos(), 126 } 127 args[1] = tc.n.Expr 128 // Replace our node with a call to the proper function. This isn't 129 // type checked but we already verified types. 130 return &ast.Call{ 131 Func: mathFunc, 132 Args: args, 133 Posx: tc.n.Pos(), 134 }, nil 135 } 136 137 type typeCheckArithmetic struct { 138 n *ast.Arithmetic 139 } 140 141 func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { 142 // The arguments are on the stack in reverse order, so pop them off. 143 exprs := make([]ast.Type, len(tc.n.Exprs)) 144 for i, _ := range tc.n.Exprs { 145 exprs[len(tc.n.Exprs)-1-i] = v.StackPop() 146 } 147 148 // Determine the resulting type we want. We do this by going over 149 // every expression until we find one with a type we recognize. 150 // We do this because the first expr might be a string ("var.foo") 151 // and we need to know what to implicit to. 152 mathFunc := "__builtin_IntMath" 153 mathType := ast.TypeInt 154 for _, v := range exprs { 155 exit := true 156 switch v { 157 case ast.TypeInt: 158 mathFunc = "__builtin_IntMath" 159 mathType = v 160 case ast.TypeFloat: 161 mathFunc = "__builtin_FloatMath" 162 mathType = v 163 default: 164 exit = false 165 } 166 167 // We found the type, so leave 168 if exit { 169 break 170 } 171 } 172 173 // Verify the args 174 for i, arg := range exprs { 175 if arg != mathType { 176 cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i]) 177 if cn != nil { 178 tc.n.Exprs[i] = cn 179 continue 180 } 181 182 return nil, fmt.Errorf( 183 "operand %d should be %s, got %s", 184 i+1, mathType, arg) 185 } 186 } 187 188 // Modulo doesn't work for floats 189 if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod { 190 return nil, fmt.Errorf("modulo cannot be used with floats") 191 } 192 193 // Return type 194 v.StackPush(mathType) 195 196 // Replace our node with a call to the proper function. This isn't 197 // type checked but we already verified types. 198 args := make([]ast.Node, len(tc.n.Exprs)+1) 199 args[0] = &ast.LiteralNode{ 200 Value: tc.n.Op, 201 Typex: ast.TypeInt, 202 Posx: tc.n.Pos(), 203 } 204 copy(args[1:], tc.n.Exprs) 205 return &ast.Call{ 206 Func: mathFunc, 207 Args: args, 208 Posx: tc.n.Pos(), 209 }, nil 210 } 211 212 type typeCheckCall struct { 213 n *ast.Call 214 } 215 216 func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { 217 // Look up the function in the map 218 function, ok := v.Scope.LookupFunc(tc.n.Func) 219 if !ok { 220 return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) 221 } 222 223 // The arguments are on the stack in reverse order, so pop them off. 224 args := make([]ast.Type, len(tc.n.Args)) 225 for i, _ := range tc.n.Args { 226 args[len(tc.n.Args)-1-i] = v.StackPop() 227 } 228 229 // Verify the args 230 for i, expected := range function.ArgTypes { 231 if expected == ast.TypeAny { 232 continue 233 } 234 235 if args[i] != expected { 236 cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) 237 if cn != nil { 238 tc.n.Args[i] = cn 239 continue 240 } 241 242 return nil, fmt.Errorf( 243 "%s: argument %d should be %s, got %s", 244 tc.n.Func, i+1, expected, args[i]) 245 } 246 } 247 248 // If we're variadic, then verify the types there 249 if function.Variadic && function.VariadicType != ast.TypeAny { 250 args = args[len(function.ArgTypes):] 251 for i, t := range args { 252 if t != function.VariadicType { 253 realI := i + len(function.ArgTypes) 254 cn := v.ImplicitConversion( 255 t, function.VariadicType, tc.n.Args[realI]) 256 if cn != nil { 257 tc.n.Args[realI] = cn 258 continue 259 } 260 261 return nil, fmt.Errorf( 262 "%s: argument %d should be %s, got %s", 263 tc.n.Func, realI, 264 function.VariadicType, t) 265 } 266 } 267 } 268 269 // Return type 270 v.StackPush(function.ReturnType) 271 272 return tc.n, nil 273 } 274 275 type typeCheckConcat struct { 276 n *ast.Concat 277 } 278 279 func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) { 280 n := tc.n 281 types := make([]ast.Type, len(n.Exprs)) 282 for i, _ := range n.Exprs { 283 types[len(n.Exprs)-1-i] = v.StackPop() 284 } 285 286 // All concat args must be strings, so validate that 287 for i, t := range types { 288 if t != ast.TypeString { 289 cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) 290 if cn != nil { 291 n.Exprs[i] = cn 292 continue 293 } 294 295 return nil, fmt.Errorf( 296 "argument %d must be a string", i+1) 297 } 298 } 299 300 // This always results in type string 301 v.StackPush(ast.TypeString) 302 303 return n, nil 304 } 305 306 type typeCheckLiteral struct { 307 n *ast.LiteralNode 308 } 309 310 func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { 311 v.StackPush(tc.n.Typex) 312 return tc.n, nil 313 } 314 315 type typeCheckVariableAccess struct { 316 n *ast.VariableAccess 317 } 318 319 func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { 320 // Look up the variable in the map 321 variable, ok := v.Scope.LookupVar(tc.n.Name) 322 if !ok { 323 return nil, fmt.Errorf( 324 "unknown variable accessed: %s", tc.n.Name) 325 } 326 327 // Add the type to the stack 328 v.StackPush(variable.Type) 329 330 return tc.n, nil 331 } 332 333 func (v *TypeCheck) ImplicitConversion( 334 actual ast.Type, expected ast.Type, n ast.Node) ast.Node { 335 if v.Implicit == nil { 336 return nil 337 } 338 339 fromMap, ok := v.Implicit[actual] 340 if !ok { 341 return nil 342 } 343 344 toFunc, ok := fromMap[expected] 345 if !ok { 346 return nil 347 } 348 349 return &ast.Call{ 350 Func: toFunc, 351 Args: []ast.Node{n}, 352 Posx: n.Pos(), 353 } 354 } 355 356 func (v *TypeCheck) reset() { 357 v.Stack = nil 358 v.err = nil 359 } 360 361 func (v *TypeCheck) StackPush(t ast.Type) { 362 v.Stack = append(v.Stack, t) 363 } 364 365 func (v *TypeCheck) StackPop() ast.Type { 366 var x ast.Type 367 x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] 368 return x 369 }