diff --git a/spec/compiler/codegen/cast_spec.cr b/spec/compiler/codegen/cast_spec.cr index cbfba96ff70e..9dcf115329ea 100644 --- a/spec/compiler/codegen/cast_spec.cr +++ b/spec/compiler/codegen/cast_spec.cr @@ -497,4 +497,15 @@ describe "Code gen: cast" do Base.as(Base | Base.class).as?(Base | Impl).nil? CRYSTAL end + + it "casts same-type tuple to Enumerable whose splat collapses (#8771)" do + # `Tuple(String, String)` is instantiated early (via `ENV`) before + # `Tuple` includes `Indexable`, so it used to be missing from + # `Enumerable(String).including_types`, crashing this upcast. + run(<<-CRYSTAL).to_string.should eq("ab") + require "prelude" + + ({"a", "b"}.as(Enumerable(String))).to_a.join + CRYSTAL + end end diff --git a/src/prelude.cr b/src/prelude.cr index 267bb7eb1f48..c5beefe4a8aa 100644 --- a/src/prelude.cr +++ b/src/prelude.cr @@ -18,6 +18,13 @@ require "iterable" require "iterator" require "steppable" require "indexable" +# `Tuple` includes `Indexable(Union(*T))`. It must be required here, before any +# file that can register a tuple as an including type of a module (e.g. `env`'s +# `extend Enumerable(...)`), otherwise that early instantiation runs with an +# incomplete parent list and is missing from the module's including types, +# breaking `as Enumerable(T)` upcasts (#8771). +require "tuple" +require "named_tuple" require "string" require "number" require "primitives" @@ -53,7 +60,6 @@ require "io" require "kernel" require "math/math" require "mutex" -require "named_tuple" require "nil" require "humanize" require "path" @@ -78,7 +84,6 @@ require "symbol" require "system" require "crystal/system/thread" require "time" -require "tuple" require "unicode" require "union" require "va_list"