Skip to content

Commit

Permalink
Allow macro calls on generic type receivers
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Sep 18, 2024
1 parent f17b565 commit 06d56b8
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 7 deletions.
40 changes: 40 additions & 0 deletions spec/compiler/semantic/macro_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,46 @@ describe "Semantic: macro" do
CRYSTAL
end

it "finds macro through generic type" do
assert_type(<<-CRYSTAL) { int32.metaclass }
module Foo(T)
macro foo
{{ T }}
end
end
Foo(Int32).foo
CRYSTAL
end

it "finds macro through alias to generic instance" do
assert_type(<<-CRYSTAL) { int32.metaclass }
module Foo(T)
macro foo
{{ T }}
end
end
alias Bar = Foo(Int32)
Bar.foo
CRYSTAL
end

it "finds macro through alias to generic type" do
assert_type(<<-CRYSTAL) { int32.metaclass }
module Foo(T)
macro foo
{{ T }}
end
end
alias Bar = Foo
Bar(Int32).foo
CRYSTAL
end

it "can override macro (#2773)" do
assert_type(<<-CRYSTAL) { char }
macro foo
Expand Down
10 changes: 8 additions & 2 deletions src/compiler/crystal/semantic/semantic_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,15 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor

obj = node.obj
case obj
when Path
when Path, Generic
base_type = @path_lookup || @scope || @current_type
macro_scope = base_type.lookup_type_var?(obj, free_vars: free_vars, raise: raise_on_missing_const)
macro_scope =
if obj.is_a?(Path)
base_type.lookup_type_var?(obj, free_vars: free_vars, raise: raise_on_missing_const)
else
base_type.lookup_type?(obj, allow_typeof: false, free_vars: free_vars, raise: raise_on_missing_const, raise_typeof: false)
end

return false unless macro_scope.is_a?(Type)

macro_scope = macro_scope.remove_alias
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/crystal/semantic/type_lookup.cr
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class Crystal::Type
end

# Similar to `lookup_type`, but returns `nil` if a type can't be found.
def lookup_type?(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, find_root_generic_type_parameters = true) : Type?
TypeLookup.new(self, self_type, false, allow_typeof, free_vars, find_root_generic_type_parameters).lookup(node)
def lookup_type?(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, raise = false, raise_typeof = raise, find_root_generic_type_parameters = true) : Type?
TypeLookup.new(self, self_type, raise, allow_typeof, free_vars, find_root_generic_type_parameters, raise_typeof: raise_typeof).lookup(node)
end

# Similar to `lookup_type`, but the result might also be an ASTNode, for example when
Expand All @@ -62,7 +62,7 @@ class Crystal::Type
end

private struct TypeLookup
def initialize(@root : Type, @self_type : Type, @raise : Bool, @allow_typeof : Bool, @free_vars : Hash(String, TypeVar)? = nil, @find_root_generic_type_parameters = true, @remove_alias = true)
def initialize(@root : Type, @self_type : Type, @raise : Bool, @allow_typeof : Bool, @free_vars : Hash(String, TypeVar)? = nil, @find_root_generic_type_parameters = true, @remove_alias = true, @raise_typeof : Bool = raise)
@in_generic_args = 0

# If we are looking types inside a non-instantiated generic type,
Expand Down Expand Up @@ -274,7 +274,7 @@ class Crystal::Type
end

type = in_generic_args { lookup(type_var) }
return if !@raise && !type
return if (!@raise || !@raise_typeof) && !type
type = type.not_nil!

case instance_type
Expand Down Expand Up @@ -369,7 +369,7 @@ class Crystal::Type

def lookup(node : TypeOf)
unless @allow_typeof
if @raise
if @raise_typeof
node.raise "can't use 'typeof' here"
else
return
Expand Down

0 comments on commit 06d56b8

Please sign in to comment.