github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/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 104 mathFunc := "__builtin_IntMath" 105 mathType := ast.TypeInt 106 switch v := exprs[0]; v { 107 case ast.TypeInt: 108 mathFunc = "__builtin_IntMath" 109 mathType = v 110 case ast.TypeFloat: 111 mathFunc = "__builtin_FloatMath" 112 mathType = v 113 default: 114 return nil, fmt.Errorf( 115 "Math operations can only be done with ints and floats, got %s", 116 v) 117 } 118 119 // Verify the args 120 for i, arg := range exprs { 121 if arg != mathType { 122 cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i]) 123 if cn != nil { 124 tc.n.Exprs[i] = cn 125 continue 126 } 127 128 return nil, fmt.Errorf( 129 "operand %d should be %s, got %s", 130 i+1, mathType, arg) 131 } 132 } 133 134 // Modulo doesn't work for floats 135 if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod { 136 return nil, fmt.Errorf("modulo cannot be used with floats") 137 } 138 139 // Return type 140 v.StackPush(mathType) 141 142 // Replace our node with a call to the proper function. This isn't 143 // type checked but we already verified types. 144 args := make([]ast.Node, len(tc.n.Exprs)+1) 145 args[0] = &ast.LiteralNode{ 146 Value: tc.n.Op, 147 Typex: ast.TypeInt, 148 Posx: tc.n.Pos(), 149 } 150 copy(args[1:], tc.n.Exprs) 151 return &ast.Call{ 152 Func: mathFunc, 153 Args: args, 154 Posx: tc.n.Pos(), 155 }, nil 156 } 157 158 type typeCheckCall struct { 159 n *ast.Call 160 } 161 162 func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { 163 // Look up the function in the map 164 function, ok := v.Scope.LookupFunc(tc.n.Func) 165 if !ok { 166 return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) 167 } 168 169 // The arguments are on the stack in reverse order, so pop them off. 170 args := make([]ast.Type, len(tc.n.Args)) 171 for i, _ := range tc.n.Args { 172 args[len(tc.n.Args)-1-i] = v.StackPop() 173 } 174 175 // Verify the args 176 for i, expected := range function.ArgTypes { 177 if expected == ast.TypeAny { 178 continue 179 } 180 181 if args[i] != expected { 182 cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) 183 if cn != nil { 184 tc.n.Args[i] = cn 185 continue 186 } 187 188 return nil, fmt.Errorf( 189 "%s: argument %d should be %s, got %s", 190 tc.n.Func, i+1, expected, args[i]) 191 } 192 } 193 194 // If we're variadic, then verify the types there 195 if function.Variadic && function.VariadicType != ast.TypeAny { 196 args = args[len(function.ArgTypes):] 197 for i, t := range args { 198 if t != function.VariadicType { 199 realI := i + len(function.ArgTypes) 200 cn := v.ImplicitConversion( 201 t, function.VariadicType, tc.n.Args[realI]) 202 if cn != nil { 203 tc.n.Args[realI] = cn 204 continue 205 } 206 207 return nil, fmt.Errorf( 208 "%s: argument %d should be %s, got %s", 209 tc.n.Func, realI, 210 function.VariadicType, t) 211 } 212 } 213 } 214 215 // Return type 216 v.StackPush(function.ReturnType) 217 218 return tc.n, nil 219 } 220 221 type typeCheckConcat struct { 222 n *ast.Concat 223 } 224 225 func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) { 226 n := tc.n 227 types := make([]ast.Type, len(n.Exprs)) 228 for i, _ := range n.Exprs { 229 types[len(n.Exprs)-1-i] = v.StackPop() 230 } 231 232 // All concat args must be strings, so validate that 233 for i, t := range types { 234 if t != ast.TypeString { 235 cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) 236 if cn != nil { 237 n.Exprs[i] = cn 238 continue 239 } 240 241 return nil, fmt.Errorf( 242 "argument %d must be a string", i+1) 243 } 244 } 245 246 // This always results in type string 247 v.StackPush(ast.TypeString) 248 249 return n, nil 250 } 251 252 type typeCheckLiteral struct { 253 n *ast.LiteralNode 254 } 255 256 func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { 257 v.StackPush(tc.n.Typex) 258 return tc.n, nil 259 } 260 261 type typeCheckVariableAccess struct { 262 n *ast.VariableAccess 263 } 264 265 func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { 266 // Look up the variable in the map 267 variable, ok := v.Scope.LookupVar(tc.n.Name) 268 if !ok { 269 return nil, fmt.Errorf( 270 "unknown variable accessed: %s", tc.n.Name) 271 } 272 273 // Add the type to the stack 274 v.StackPush(variable.Type) 275 276 return tc.n, nil 277 } 278 279 func (v *TypeCheck) ImplicitConversion( 280 actual ast.Type, expected ast.Type, n ast.Node) ast.Node { 281 if v.Implicit == nil { 282 return nil 283 } 284 285 fromMap, ok := v.Implicit[actual] 286 if !ok { 287 return nil 288 } 289 290 toFunc, ok := fromMap[expected] 291 if !ok { 292 return nil 293 } 294 295 return &ast.Call{ 296 Func: toFunc, 297 Args: []ast.Node{n}, 298 Posx: n.Pos(), 299 } 300 } 301 302 func (v *TypeCheck) reset() { 303 v.Stack = nil 304 v.err = nil 305 } 306 307 func (v *TypeCheck) StackPush(t ast.Type) { 308 v.Stack = append(v.Stack, t) 309 } 310 311 func (v *TypeCheck) StackPop() ast.Type { 312 var x ast.Type 313 x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] 314 return x 315 }