Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error if abstract def implementation is inherited from supertype #11056

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 70 additions & 19 deletions spec/compiler/semantic/abstract_def_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -604,25 +604,6 @@ describe "Semantic: abstract def" do
"can't resolve return type Unknown"
end

it "doesn't crash when abstract method is implemented by supertype (#8031)" do
semantic(%(
module Base(T)
def size
end
end

module Child(T)
include Base(T)

abstract def size
end

class Foo
include Child(Int32)
end
))
end

it "implements through extend (considers original type for generic lookup) (#8096)" do
semantic(%(
module ICallable(T)
Expand Down Expand Up @@ -1045,4 +1026,74 @@ describe "Semantic: abstract def" do
end
)
end

describe "implementation is not inherited from supertype" do
it "nongeneric class" do
assert_error <<-CR, "abstract `def Abstract#foo()` must be implemented by Concrete", inject_primitives: false
class Supertype
def foo; end
end

abstract class Abstract < Supertype
abstract def foo
end

class Concrete < Abstract
end
CR
end

it "generic class" do
assert_error <<-CR, "abstract `def Abstract(T)#foo()` must be implemented by Concrete", inject_primitives: false
class Supertype(T)
def foo; end
end

abstract class Abstract(T) < Supertype(T)
abstract def foo
end

class Concrete(T) < Abstract(T)
end
CR
end

it "nongeneric module" do
assert_error <<-CR, "abstract `def Abstract#size()` must be implemented by Concrete", inject_primitives: false
module Supertype
def size
end
end

module Abstract
include Supertype

abstract def size
end

class Concrete
include Abstract
end
CR
end

it "generic module" do
assert_error <<-CR, "abstract `def Abstract(T)#size()` must be implemented by Concrete(T)", inject_primitives: false
module Supertype(T)
def size
end
end

module Abstract(T)
include Supertype(T)

abstract def size
end

class Concrete(T)
include Abstract(T)
end
CR
end
end
end
10 changes: 9 additions & 1 deletion src/compiler/crystal/semantic/abstract_def_checker.cr
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,15 @@ class Crystal::AbstractDefChecker
return true if implements?(type, type, method, base, free_vars)

type.ancestors.any? do |ancestor|
implements?(type, ancestor, method, base, free_vars)
if implements?(type, ancestor, method, base, free_vars)
# Check that the implementation does not come from a supertype of `base`
if ancestor.is_a?(GenericInstanceType)
ancestor = ancestor.generic_type.as(Type)
end
!base.implements?(ancestor)
else
false
end
end
end

Expand Down