diff --git a/cty/function/stdlib/sequence.go b/cty/function/stdlib/sequence.go index d3cc341d..8cfed57f 100644 --- a/cty/function/stdlib/sequence.go +++ b/cty/function/stdlib/sequence.go @@ -11,8 +11,9 @@ import ( var ConcatFunc = function.New(&function.Spec{ Params: []function.Parameter{}, VarParam: &function.Parameter{ - Name: "seqs", - Type: cty.DynamicPseudoType, + Name: "seqs", + Type: cty.DynamicPseudoType, + AllowMarked: true, }, Type: func(args []cty.Value) (ret cty.Type, err error) { if len(args) == 0 { @@ -75,6 +76,7 @@ var ConcatFunc = function.New(&function.Spec{ // given values will be lists and that they will either be of // retType or of something we can convert to retType. vals := make([]cty.Value, 0, len(args)) + var markses []cty.ValueMarks // remember any marked lists we find for i, list := range args { list, err = convert.Convert(list, retType) if err != nil { @@ -83,6 +85,11 @@ var ConcatFunc = function.New(&function.Spec{ return cty.NilVal, function.NewArgError(i, err) } + list, listMarks := list.Unmark() + if len(listMarks) > 0 { + markses = append(markses, listMarks) + } + it := list.ElementIterator() for it.Next() { _, v := it.Element() @@ -90,10 +97,10 @@ var ConcatFunc = function.New(&function.Spec{ } } if len(vals) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil + return cty.ListValEmpty(retType.ElementType()).WithMarks(markses...), nil } - return cty.ListVal(vals), nil + return cty.ListVal(vals).WithMarks(markses...), nil case retType.IsTupleType(): // If retType is a tuple type then we could have a mixture of // lists and tuples but we know they all have known values @@ -101,8 +108,14 @@ var ConcatFunc = function.New(&function.Spec{ // concatenating them all together will produce a tuple of // retType because of the work we did in the Type function above. vals := make([]cty.Value, 0, len(args)) + var markses []cty.ValueMarks // remember any marked seqs we find for _, seq := range args { + seq, seqMarks := seq.Unmark() + if len(seqMarks) > 0 { + markses = append(markses, seqMarks) + } + // Both lists and tuples support ElementIterator, so this is easy. it := seq.ElementIterator() for it.Next() { @@ -111,7 +124,7 @@ var ConcatFunc = function.New(&function.Spec{ } } - return cty.TupleVal(vals), nil + return cty.TupleVal(vals).WithMarks(markses...), nil default: // should never happen if Type is working correctly above panic("unsupported return type") diff --git a/cty/function/stdlib/sequence_test.go b/cty/function/stdlib/sequence_test.go index d390ad28..483ea97d 100644 --- a/cty/function/stdlib/sequence_test.go +++ b/cty/function/stdlib/sequence_test.go @@ -48,6 +48,54 @@ func TestConcat(t *testing.T) { cty.NumberIntVal(3), }), }, + { + []cty.Value{ + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }), + cty.ListVal([]cty.Value{ + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }).Mark("a"), + }, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }).Mark("a"), + }, + { + []cty.Value{ + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }), + cty.ListVal([]cty.Value{ + cty.NumberIntVal(2).Mark("b"), + cty.NumberIntVal(3), + }), + }, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2).Mark("b"), + cty.NumberIntVal(3), + }), + }, + { + []cty.Value{ + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }).Mark("a"), + cty.ListVal([]cty.Value{ + cty.NumberIntVal(2).Mark("b"), + cty.NumberIntVal(3), + }), + }, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2).Mark("b"), + cty.NumberIntVal(3), + }).Mark("a"), + }, { []cty.Value{ cty.ListVal([]cty.Value{