diff --git a/spec/compiler/semantic/alias_spec.cr b/spec/compiler/semantic/alias_spec.cr index 4005bfd61606..eecd1e22034c 100644 --- a/spec/compiler/semantic/alias_spec.cr +++ b/spec/compiler/semantic/alias_spec.cr @@ -297,4 +297,17 @@ describe "Semantic: alias" do ), "undefined constant T" end + + it "doesn't crash by infinite recursion against type alias and generics (#5329)" do + assert_error %( + class Foo(T) + def initialize(@foo : T) + end + end + + alias Bar = Foo(Bar | Int32) + + Foo(Bar).new(Foo.new(1).as(Bar)) + ), "can't cast Foo(Int32) to Bar" + end end diff --git a/src/compiler/crystal/semantic/restrictions.cr b/src/compiler/crystal/semantic/restrictions.cr index 6ad1b6d19f44..99ecb1e3f2cd 100644 --- a/src/compiler/crystal/semantic/restrictions.cr +++ b/src/compiler/crystal/semantic/restrictions.cr @@ -667,7 +667,10 @@ module Crystal elsif context.strict? type_var == other_type_var else - type_var.restrict(other_type_var, context) == type_var + # To prevent infinite recursion, it checks equality between + # `type_var` and `other_type_var` directly before try to restrict + # `type_var` by `other_type_var`. + type_var == other_type_var || type_var.restrict(other_type_var, context) == type_var end end end