diff --git a/vendor/github.com/hashicorp/hil/ast/literal.go b/vendor/github.com/hashicorp/hil/ast/literal.go index 8149d495d..da6014fee 100644 --- a/vendor/github.com/hashicorp/hil/ast/literal.go +++ b/vendor/github.com/hashicorp/hil/ast/literal.go @@ -77,3 +77,12 @@ func (n *LiteralNode) String() string { func (n *LiteralNode) Type(Scope) (Type, error) { return n.Typex, nil } + +// IsUnknown returns true either if the node's value is itself unknown +// of if it is a collection containing any unknown elements, deeply. +func (n *LiteralNode) IsUnknown() bool { + return IsUnknown(Variable{ + Type: n.Typex, + Value: n.Value, + }) +} diff --git a/vendor/github.com/hashicorp/hil/ast/variables_helper.go b/vendor/github.com/hashicorp/hil/ast/variables_helper.go index 40aa534cf..06bd18de2 100644 --- a/vendor/github.com/hashicorp/hil/ast/variables_helper.go +++ b/vendor/github.com/hashicorp/hil/ast/variables_helper.go @@ -3,54 +3,61 @@ package ast import "fmt" func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) { - listTypes := make(map[Type]struct{}) + if len(list) == 0 { + return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName) + } + + elemType := TypeUnknown for _, v := range list { - // Allow unknown if v.Type == TypeUnknown { continue } - if _, ok := listTypes[v.Type]; ok { + if elemType == TypeUnknown { + elemType = v.Type continue } - listTypes[v.Type] = struct{}{} + + if v.Type != elemType { + return TypeInvalid, fmt.Errorf( + "list %q does not have homogenous types. found %s and then %s", + variableName, + elemType, v.Type, + ) + } + + elemType = v.Type } - if len(listTypes) != 1 && len(list) != 0 { - return TypeInvalid, fmt.Errorf("list %q does not have homogenous types. found %s", variableName, reportTypes(listTypes)) - } - - if len(list) > 0 { - return list[0].Type, nil - } - - return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName) + return elemType, nil } func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) { - valueTypes := make(map[Type]struct{}) + if len(vmap) == 0 { + return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName) + } + + elemType := TypeUnknown for _, v := range vmap { - // Allow unknown if v.Type == TypeUnknown { continue } - if _, ok := valueTypes[v.Type]; ok { + if elemType == TypeUnknown { + elemType = v.Type continue } - valueTypes[v.Type] = struct{}{} + if v.Type != elemType { + return TypeInvalid, fmt.Errorf( + "map %q does not have homogenous types. found %s and then %s", + variableName, + elemType, v.Type, + ) + } + + elemType = v.Type } - if len(valueTypes) != 1 && len(vmap) != 0 { - return TypeInvalid, fmt.Errorf("map %q does not have homogenous value types. found %s", variableName, reportTypes(valueTypes)) - } - - // For loop here is an easy way to get a single key, we return immediately. - for _, v := range vmap { - return v.Type, nil - } - - // This means the map is empty - return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName) + return elemType, nil } diff --git a/vendor/github.com/hashicorp/hil/check_types.go b/vendor/github.com/hashicorp/hil/check_types.go index a8ca44e06..7a191e877 100644 --- a/vendor/github.com/hashicorp/hil/check_types.go +++ b/vendor/github.com/hashicorp/hil/check_types.go @@ -98,10 +98,6 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node { pos.Column, pos.Line, err) } - if v.StackPeek() == ast.TypeUnknown { - v.err = errExitUnknown - } - return result } @@ -116,6 +112,14 @@ func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { exprs[len(tc.n.Exprs)-1-i] = v.StackPop() } + // If any operand is unknown then our result is automatically unknown + for _, ty := range exprs { + if ty == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + } + switch tc.n.Op { case ast.ArithmeticOpLogicalAnd, ast.ArithmeticOpLogicalOr: return tc.checkLogical(v, exprs) @@ -333,6 +337,11 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { continue } + if args[i] == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + if args[i] != expected { cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) if cn != nil { @@ -350,6 +359,11 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { if function.Variadic && function.VariadicType != ast.TypeAny { args = args[len(function.ArgTypes):] for i, t := range args { + if t == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + if t != function.VariadicType { realI := i + len(function.ArgTypes) cn := v.ImplicitConversion( @@ -384,6 +398,11 @@ func (tc *typeCheckConditional) TypeCheck(v *TypeCheck) (ast.Node, error) { trueType := v.StackPop() condType := v.StackPop() + if condType == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + if condType != ast.TypeBool { cn := v.ImplicitConversion(condType, ast.TypeBool, tc.n.CondExpr) if cn == nil { @@ -457,6 +476,13 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) { types[len(n.Exprs)-1-i] = v.StackPop() } + for _, ty := range types { + if ty == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + } + // If there is only one argument and it is a list, we evaluate to a list if len(types) == 1 { switch t := types[0]; t { @@ -469,7 +495,14 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) { } // Otherwise, all concat args must be strings, so validate that + resultType := ast.TypeString for i, t := range types { + + if t == ast.TypeUnknown { + resultType = ast.TypeUnknown + continue + } + if t != ast.TypeString { cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) if cn != nil { @@ -482,8 +515,8 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) { } } - // This always results in type string - v.StackPush(ast.TypeString) + // This always results in type string, unless there are unknowns + v.StackPush(resultType) return n, nil } @@ -509,13 +542,6 @@ func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { "unknown variable accessed: %s", tc.n.Name) } - // Check if the variable contains any unknown types. If so, then - // mark it as unknown. - if ast.IsUnknown(variable) { - v.StackPush(ast.TypeUnknown) - return tc.n, nil - } - // Add the type to the stack v.StackPush(variable.Type) @@ -530,6 +556,11 @@ func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) { keyType := v.StackPop() targetType := v.StackPop() + if keyType == ast.TypeUnknown || targetType == ast.TypeUnknown { + v.StackPush(ast.TypeUnknown) + return tc.n, nil + } + // Ensure we have a VariableAccess as the target varAccessNode, ok := tc.n.Target.(*ast.VariableAccess) if !ok { diff --git a/vendor/github.com/hashicorp/hil/eval.go b/vendor/github.com/hashicorp/hil/eval.go index 5c7afb024..27820769e 100644 --- a/vendor/github.com/hashicorp/hil/eval.go +++ b/vendor/github.com/hashicorp/hil/eval.go @@ -54,6 +54,14 @@ func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) { return InvalidResult, err } + // If the result contains any nested unknowns then the result as a whole + // is unknown, so that callers only have to deal with "entirely known" + // or "entirely unknown" as outcomes. + if ast.IsUnknown(ast.Variable{Type: outputType, Value: output}) { + outputType = ast.TypeUnknown + output = UnknownValue + } + switch outputType { case ast.TypeList: val, err := VariableToInterface(ast.Variable{ @@ -264,6 +272,10 @@ func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, e args := make([]interface{}, len(v.Args)) for i, _ := range v.Args { node := stack.Pop().(*ast.LiteralNode) + if node.IsUnknown() { + // If any arguments are unknown then the result is automatically unknown + return UnknownValue, ast.TypeUnknown, nil + } args[len(v.Args)-1-i] = node.Value } @@ -286,6 +298,11 @@ func (v *evalConditional) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast. trueLit := stack.Pop().(*ast.LiteralNode) condLit := stack.Pop().(*ast.LiteralNode) + if condLit.IsUnknown() { + // If our conditional is unknown then our result is also unknown + return UnknownValue, ast.TypeUnknown, nil + } + if condLit.Value.(bool) { return trueLit.Value, trueLit.Typex, nil } else { @@ -301,6 +318,17 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty variableName := v.Index.Target.(*ast.VariableAccess).Name + if key.IsUnknown() { + // If our key is unknown then our result is also unknown + return UnknownValue, ast.TypeUnknown, nil + } + + // For target, we'll accept collections containing unknown values but + // we still need to catch when the collection itself is unknown, shallowly. + if target.Typex == ast.TypeUnknown { + return UnknownValue, ast.TypeUnknown, nil + } + switch target.Typex { case ast.TypeList: return v.evalListIndex(variableName, target.Value, key.Value) @@ -377,8 +405,22 @@ func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, // The expressions should all be on the stack in reverse // order. So pop them off, reverse their order, and concatenate. nodes := make([]*ast.LiteralNode, 0, len(v.Exprs)) + haveUnknown := false for range v.Exprs { - nodes = append(nodes, stack.Pop().(*ast.LiteralNode)) + n := stack.Pop().(*ast.LiteralNode) + nodes = append(nodes, n) + + // If we have any unknowns then the whole result is unknown + // (we must deal with this first, because the type checker can + // skip type conversions in the presence of unknowns, and thus + // any of our other nodes may be incorrectly typed.) + if n.IsUnknown() { + haveUnknown = true + } + } + + if haveUnknown { + return UnknownValue, ast.TypeUnknown, nil } // Special case the single list and map @@ -396,6 +438,14 @@ func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, // Otherwise concatenate the strings var buf bytes.Buffer for i := len(nodes) - 1; i >= 0; i-- { + if nodes[i].Typex != ast.TypeString { + return nil, ast.TypeInvalid, fmt.Errorf( + "invalid output with %s value at index %d: %#v", + nodes[i].Typex, + i, + nodes[i].Value, + ) + } buf.WriteString(nodes[i].Value.(string)) } @@ -418,11 +468,5 @@ func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, a "unknown variable accessed: %s", v.Name) } - // Check if the variable contains any unknown types. If so, then - // mark it as unknown and return that type. - if ast.IsUnknown(variable) { - return nil, ast.TypeUnknown, nil - } - return variable.Value, variable.Type, nil } diff --git a/vendor/vendor.json b/vendor/vendor.json index b40002ee5..912393487 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2138,28 +2138,28 @@ "revisionTime": "2017-05-04T19:02:34Z" }, { - "checksumSHA1": "2Nrl/YKrmowkRgCDLhA6UTFgYEY=", + "checksumSHA1": "zz3/f3YpHHBN78uLhnhLBW2aF8o=", "path": "github.com/hashicorp/hil", - "revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2", - "revisionTime": "2016-12-21T19:20:42Z" + "revision": "747a6e1523d6808f91144df070435b16865cd333", + "revisionTime": "2017-05-01T20:07:50Z" }, { - "checksumSHA1": "oZ2a2x9qyHqvqJdv/Du3LGeaFdA=", + "checksumSHA1": "0S0KeBcfqVFYBPeZkuJ4fhQ5mCA=", "path": "github.com/hashicorp/hil/ast", - "revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2", - "revisionTime": "2016-12-21T19:20:42Z" + "revision": "747a6e1523d6808f91144df070435b16865cd333", + "revisionTime": "2017-05-01T20:07:50Z" }, { "checksumSHA1": "P5PZ3k7SmqWmxgJ8Q0gLzeNpGhE=", "path": "github.com/hashicorp/hil/parser", - "revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2", - "revisionTime": "2016-12-21T19:20:42Z" + "revision": "747a6e1523d6808f91144df070435b16865cd333", + "revisionTime": "2017-05-01T20:07:50Z" }, { "checksumSHA1": "DC1k5kOua4oFqmo+JRt0YzfP44o=", "path": "github.com/hashicorp/hil/scanner", - "revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2", - "revisionTime": "2016-12-21T19:20:42Z" + "revision": "747a6e1523d6808f91144df070435b16865cd333", + "revisionTime": "2017-05-01T20:07:50Z" }, { "checksumSHA1": "vt+P9D2yWDO3gdvdgCzwqunlhxU=",