diff --git a/cty/function/stdlib/collection.go b/cty/function/stdlib/collection.go index 279a20ee..a91821e9 100644 --- a/cty/function/stdlib/collection.go +++ b/cty/function/stdlib/collection.go @@ -525,6 +525,7 @@ func flattener(flattenList cty.Value) ([]cty.Value, []cty.ValueMarks, bool) { if len(flattenListMarks) > 0 { markses = append(markses, flattenListMarks) } + if !flattenList.Length().IsKnown() { // If we don't know the length of what we're flattening then we can't // predict the length of our result yet either. @@ -542,7 +543,7 @@ func flattener(flattenList cty.Value) ([]cty.Value, []cty.ValueMarks, bool) { isKnown = false } - if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() { + if !val.IsNull() && (val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType()) { if !val.IsKnown() { isKnown = false _, unknownMarks := val.Unmark() diff --git a/cty/function/stdlib/collection_test.go b/cty/function/stdlib/collection_test.go index 0d5d5e83..ebcb6ea4 100644 --- a/cty/function/stdlib/collection_test.go +++ b/cty/function/stdlib/collection_test.go @@ -2064,6 +2064,110 @@ func TestFlatten(t *testing.T) { cty.UnknownVal(cty.DynamicPseudoType), "", }, + { + // null of an unknown type + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.DynamicPseudoType), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.DynamicPseudoType), + cty.True, + }), + "", + }, + { + // null of a string type + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.True, + }), + "", + }, + { + // null of a list type + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.List(cty.String)), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.List(cty.String)), + cty.True, + }), + "", + }, + { + // null of a tuple type + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.EmptyTuple), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.EmptyTuple), + cty.True, + }), + "", + }, + { + // nested null of an unknown type + cty.TupleVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.DynamicPseudoType), + }), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.DynamicPseudoType), + cty.True, + }), + "", + }, + { + // nested null of a string type + cty.TupleVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + }), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.True, + }), + "", + }, + { + // nested null of a list type + cty.TupleVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.List(cty.String)), + }), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.List(cty.String)), + cty.True, + }), + "", + }, + { + // nested null of a tuple type + cty.TupleVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.EmptyTuple), + }), + cty.True, + }), + cty.TupleVal([]cty.Value{ + cty.NullVal(cty.EmptyTuple), + cty.True, + }), + "", + }, } for _, test := range tests {