From b6b3dba127755c253e3adda7379bd5d1a02d9401 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 11 Oct 2023 10:13:14 -0400 Subject: [PATCH] refinements of collections must use Range() When attempting to determine the final length range for a conditional expression with collections, the length values may still be unknown. Always use `Range()` to get the lower and upper bounds. --- hclsyntax/expression.go | 16 +++++++--------- hclsyntax/expression_test.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 5ed35273..71eb46ee 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -725,16 +725,14 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic falseLen := falseResult.Length() if gt := trueLen.GreaterThan(falseLen); gt.IsKnown() { b := cty.UnknownVal(resultType).Refine() - trueLen, _ := trueLen.AsBigFloat().Int64() - falseLen, _ := falseLen.AsBigFloat().Int64() if gt.True() { b = b. - CollectionLengthLowerBound(int(falseLen)). - CollectionLengthUpperBound(int(trueLen)) + CollectionLengthLowerBound(falseResult.Range().LengthLowerBound()). + CollectionLengthUpperBound(trueResult.Range().LengthUpperBound()) } else { b = b. - CollectionLengthLowerBound(int(trueLen)). - CollectionLengthUpperBound(int(falseLen)) + CollectionLengthLowerBound(trueResult.Range().LengthLowerBound()). + CollectionLengthUpperBound(falseResult.Range().LengthUpperBound()) } b = b.NotNull() // If neither of the results is null then the result can't be either return b.NewValue().WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags @@ -1244,9 +1242,9 @@ func (e *ObjectConsKeyExpr) UnwrapExpression() Expression { // ForExpr represents iteration constructs: // -// tuple = [for i, v in list: upper(v) if i > 2] -// object = {for k, v in map: k => upper(v)} -// object_of_tuples = {for v in list: v.key: v...} +// tuple = [for i, v in list: upper(v) if i > 2] +// object = {for k, v in map: k => upper(v)} +// object_of_tuples = {for v in list: v.key: v...} type ForExpr struct { KeyVar string // empty if ignoring the key ValVar string diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 1af93dad..e64047a0 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1957,6 +1957,20 @@ EOT cty.ListValEmpty(cty.String), // deduced through refinements 0, }, + { + `unknown ? ar : br`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "ar": cty.UnknownVal(cty.Set(cty.String)).Refine(). + CollectionLengthLowerBound(1).CollectionLengthUpperBound(2).NewValue(), + "br": cty.UnknownVal(cty.Set(cty.String)).Refine(). + CollectionLengthLowerBound(3).CollectionLengthUpperBound(4).NewValue(), + }, + }, + cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue(), // deduced through refinements + 0, + }, { `unknown ? a : b`, &hcl.EvalContext{