From 87c93b94a74f29717ebb648f4493107f97c1ffb6 Mon Sep 17 00:00:00 2001 From: TEC Date: Tue, 25 Jun 2024 01:22:06 +0800 Subject: [PATCH] Fix annotated join with non-concrete eltype iters As raised in , when the eltype of an iterator is non-concrete, _isannotated will return false. To properly check such cases, we need to see if any of the elements of the iterator are annotated. This is a bit of an interesting case, since: - Most of the time it shouldn't be hit, we reasonably expect most iterables to infer as producing concrete types - The eltype of the iterator is (generally) known at compile-time, and so in any case other than the ambiguous non-concrete one, this check remains able to be done at compile-time. - Should the iterator be stateful and non-concrete, the check can consume some amount of the iterator before join is called. With this change, join always preserves annotations. The compromise made is that iterators with non-concrete eltypes can result in join inferring a union return type (i.e. type instability with non-concrete iterators), but that doesn't seem too bad to me. Reported-by: kimikage --- base/strings/io.jl | 12 ++++++++---- test/strings/annotated.jl | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index 92043101297293..4ec33860940757 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -354,12 +354,16 @@ function join(io::IO, iterator, delim="") end function _join_preserve_annotations(iterator, args...) - if _isannotated(eltype(iterator)) || any(_isannotated, args) + if isconcretetype(eltype(iterator)) && !_isannotated(eltype(iterator)) && !any(_isannotated, args) + sprint(join, iterator, args...) + else io = AnnotatedIOBuffer() join(io, iterator, args...) - read(seekstart(io), AnnotatedString{String}) - else - sprint(join, iterator, args...) + if isconcretetype(eltype(iterator)) || !isempty(io.annotations) + read(seekstart(io), AnnotatedString{String}) + else + String(take!(io.io)) + end end end diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index da76f0f0205315..8eab0567221e65 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -101,6 +101,8 @@ end [(1:4, :label => 5), (5:5, :label => 2), (6:9, :label => 5)]) + @test join((String(str1), str1), ' ') == + Base.AnnotatedString("test test", [(6:9, :label => 5)]) @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label => 5)]) @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label => "oomph"), (6:7, :label => "oomph")])