Skip to content

Commit

Permalink
Fix GenericClassType vs GenericClassInstanceType restrictions (#7537)
Browse files Browse the repository at this point in the history
* Fix GenericClassType vs GenericClassInstanceType restrictions

* Simplify conditions
  • Loading branch information
bew authored and asterite committed Mar 12, 2019
1 parent b0e88cf commit 3cecde5
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 4 deletions.
90 changes: 90 additions & 0 deletions spec/compiler/semantic/restrictions_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,96 @@ describe "Restrictions" do
end
end

describe "restriction_of?" do
describe "GenericClassType vs GenericClassInstanceType" do
it "inserts GenericClassInstanceType before GenericClassType" do
assert_type(%(
class Foo(T)
end
def bar(a : Foo)
1
end
def bar(a : Foo(Int32))
true
end
{
bar(Foo(Int32).new),
bar(Foo(Float64).new)
}
)) { tuple_of([bool, int32]) }
end

it "keeps GenericClassInstanceType before GenericClassType" do
assert_type(%(
class Foo(T)
end
def bar(a : Foo(Int32))
true
end
def bar(a : Foo)
1
end
{
bar(Foo(Int32).new),
bar(Foo(Float64).new)
}
)) { tuple_of([bool, int32]) }
end

it "works with classes in different namespaces" do
assert_type(%(
class Foo(T)
end
class Mod::Foo(G)
end
def bar(a : Foo(Int32))
true
end
def bar(a : Mod::Foo)
1
end
{
bar(Foo(Int32).new),
bar(Mod::Foo(Int32).new)
}
)) { tuple_of([bool, int32]) }
end

it "doesn't mix different generic classes" do
assert_type(%(
class Foo(T)
end
class Bar(U)
end
def bar(a : Bar(Int32))
true
end
def bar(a : Foo)
1
end
{
bar(Foo(Int32).new),
bar(Bar(Int32).new)
}
)) { tuple_of([int32, bool]) }
end
end
end

it "self always matches instance type in restriction" do
assert_type(%(
class Foo
Expand Down
44 changes: 40 additions & 4 deletions src/compiler/crystal/semantic/restrictions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,10 @@ module Crystal

class Generic
def restriction_of?(other : Path, owner)
other_type = owner.lookup_type?(self)
if other_type
self_type = owner.lookup_path(other)
if self_type
self_type = owner.lookup_type?(self)
if self_type
other_type = owner.lookup_path(other)
if other_type
return self_type.restriction_of?(other_type, owner)
end
end
Expand All @@ -304,6 +304,42 @@ module Crystal
end
end

class GenericClassType
def restriction_of?(other : GenericClassInstanceType, owner)
# ```
# def foo(param : Array)
# end
#
# def foo(param : Array(Int32))
# end
# ```
#
# Here, self is `Array`, other is `Array(Int32)`

# Even when the underlying generic type is the same,
# `SomeGeneric` is never a restriction of `SomeGeneric(X)`
false
end
end

class GenericClassInstanceType
def restriction_of?(other : GenericClassType, owner)
# ```
# def foo(param : Array(Int32))
# end
#
# def foo(param : Array)
# end
# ```
#
# Here, self is `Array(Int32)`, other is `Array`

# When the underlying generic type is the same:
# `SomeGeneric(X)` is always a restriction of `SomeGeneric`
self.generic_type == other
end
end

class Metaclass
def restriction_of?(other : Metaclass, owner)
self_type = owner.lookup_type?(self)
Expand Down

0 comments on commit 3cecde5

Please sign in to comment.