diff --git a/spec/compiler/semantic/abstract_def_spec.cr b/spec/compiler/semantic/abstract_def_spec.cr index 988af2b16d32..fcf96df3b31a 100644 --- a/spec/compiler/semantic/abstract_def_spec.cr +++ b/spec/compiler/semantic/abstract_def_spec.cr @@ -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) @@ -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 diff --git a/src/compiler/crystal/semantic/abstract_def_checker.cr b/src/compiler/crystal/semantic/abstract_def_checker.cr index 03d858eae947..4ced3eaecd69 100644 --- a/src/compiler/crystal/semantic/abstract_def_checker.cr +++ b/src/compiler/crystal/semantic/abstract_def_checker.cr @@ -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