diff --git a/spec/compiler/codegen/is_a_spec.cr b/spec/compiler/codegen/is_a_spec.cr index 0eecf4bf1933..cf78d6119863 100644 --- a/spec/compiler/codegen/is_a_spec.cr +++ b/spec/compiler/codegen/is_a_spec.cr @@ -894,4 +894,15 @@ describe "Codegen: is_a?" do x.is_a?(Union(B, C)) )).to_b.should be_false end + + it "restricts union metaclass to metaclass (#12295)" do + run(%( + x = true ? Union(String | Int32) : String + if x.is_a?(String.class) + 1 + else + 2 + end + )).to_i.should eq(2) + end end diff --git a/src/compiler/crystal/semantic/type_intersect.cr b/src/compiler/crystal/semantic/type_intersect.cr index 8e15f9186cc7..8e533bd29b44 100644 --- a/src/compiler/crystal/semantic/type_intersect.cr +++ b/src/compiler/crystal/semantic/type_intersect.cr @@ -100,6 +100,12 @@ module Crystal def self.common_descendent(type1 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType, type2 : MetaclassType | VirtualMetaclassType) return type1 if type1.instance_type.generic_type.metaclass == type2 + # Special case: a GenericInstanceMetaclass with a union type is + # the metaclass of a union type, for example Union(String | Int32).class. + # In that case the intersection of that type with a non-union metaclass + # will never match: it must also be another union type. + return nil if type1.instance_type.is_a?(UnionType) + restricted = common_descendent(type1.instance_type, type2.instance_type) restricted ? type1 : nil end