From df626e898c7d52d70fbaa6d4209a05f36dcdad13 Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Thu, 10 Dec 2020 11:20:57 -0500 Subject: [PATCH] lang/funcs: Fix alltrue/anytrue with unknowns The alltrue/anytrue functions did not correctly handle unknown values. This commit changes these functions so that the result is unknown if: - The list argument is unknown - For alltrue: any elements are unknown - For anytrue: any elements are unknown and no known elements are true The last change is a little subtle, so there are test cases to cover it specifically. Examples: - anytrue(unknown) -> unknown - anytrue(false, unknown) -> unknown - anytrue(false, unknown, true) -> true --- lang/funcs/collection.go | 11 ++++++++ lang/funcs/collection_test.go | 48 +++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/lang/funcs/collection.go b/lang/funcs/collection.go index 6b8d1e6bbe..4324e589db 100644 --- a/lang/funcs/collection.go +++ b/lang/funcs/collection.go @@ -70,6 +70,9 @@ var AllTrueFunc = function.New(&function.Spec{ result := cty.True for it := args[0].ElementIterator(); it.Next(); { _, v := it.Element() + if !v.IsKnown() { + return cty.UnknownVal(cty.Bool), nil + } if v.IsNull() { return cty.False, nil } @@ -94,8 +97,13 @@ var AnyTrueFunc = function.New(&function.Spec{ Type: function.StaticReturnType(cty.Bool), Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { result := cty.False + var hasUnknown bool for it := args[0].ElementIterator(); it.Next(); { _, v := it.Element() + if !v.IsKnown() { + hasUnknown = true + continue + } if v.IsNull() { continue } @@ -104,6 +112,9 @@ var AnyTrueFunc = function.New(&function.Spec{ return cty.True, nil } } + if hasUnknown { + return cty.UnknownVal(cty.Bool), nil + } return result, nil }, }) diff --git a/lang/funcs/collection_test.go b/lang/funcs/collection_test.go index 89cb2a15f7..457ef7f735 100644 --- a/lang/funcs/collection_test.go +++ b/lang/funcs/collection_test.go @@ -169,10 +169,28 @@ func TestAllTrue(t *testing.T) { cty.False, false, }, + { + cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}), + cty.False, + false, + }, { cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), cty.UnknownVal(cty.Bool), - true, + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.UnknownVal(cty.Bool), + }), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.UnknownVal(cty.List(cty.Bool)), + cty.UnknownVal(cty.Bool), + false, }, { cty.NullVal(cty.List(cty.Bool)), @@ -232,10 +250,36 @@ func TestAnyTrue(t *testing.T) { cty.True, false, }, + { + cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}), + cty.True, + false, + }, { cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), cty.UnknownVal(cty.Bool), - true, + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.False, + }), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.True, + }), + cty.True, + false, + }, + { + cty.UnknownVal(cty.List(cty.Bool)), + cty.UnknownVal(cty.Bool), + false, }, { cty.NullVal(cty.List(cty.Bool)),