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  }