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

Compiler: refactor and slightly optimize merging two types #12436

Merged
merged 11 commits into from
Sep 7, 2022
6 changes: 3 additions & 3 deletions src/compiler/crystal/semantic/type_declaration_processor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ struct Crystal::TypeDeclarationProcessor
when NonGenericModuleType
type = type_info.type
if nilable_instance_var?(owner, name)
type = Type.merge!([type, @program.nil])
type = Type.merge!(type, @program.nil)
end

# Same as above, only Nil makes no sense
Expand All @@ -423,7 +423,7 @@ struct Crystal::TypeDeclarationProcessor
when GenericClassType
type = type_info.type
if nilable_instance_var?(owner, name)
type = Type.merge!([type, @program.nil])
type = Type.merge!(type, @program.nil)
end

# Same as above, only Nil makes no sense
Expand All @@ -442,7 +442,7 @@ struct Crystal::TypeDeclarationProcessor
when GenericModuleType
type = type_info.type
if nilable_instance_var?(owner, name)
type = Type.merge!([type, @program.nil])
type = Type.merge!(type, @program.nil)
end

declare_meta_type_var(owner.instance_vars, owner, name, type, type_info.location, instance_var: true, annotations: type_info.annotations)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/type_guess_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ module Crystal
info.add_annotations(annotations) if annotations
vars[name] = info
else
info.type = Type.merge!([info.type, type])
info.type = Type.merge!(info.type, type)
info.outside_def = true if @outside_def
info.add_annotations(annotations) if annotations
vars[name] = info
Expand Down
88 changes: 40 additions & 48 deletions src/compiler/crystal/semantic/type_merge.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,72 @@ require "../program"

module Crystal
class Program
def type_merge(types : Array(Type?))
def type_merge(types : Array(Type?)) : Type?
case types.size
when 0
return nil
nil
when 1
return types.first
types.first
when 2
# Merging two types is the most common case, so we optimize it
first, second = types
did_merge, merged_type = type_merge_two(first, second)
return merged_type if did_merge
type_merge(first, second)
else
# combined_union_of
combined_union_of compact_types(types)
end

combined_union_of compact_types(types)
end

def type_merge(nodes : Array(ASTNode))
def type_merge(nodes : Array(ASTNode)) : Type?
case nodes.size
when 0
return nil
nil
when 1
return nodes.first.type?
nodes.first.type?
when 2
# Merging two types is the most common case, so we optimize it
first, second = nodes
did_merge, merged_type = type_merge_two(first.type?, second.type?)
return merged_type if did_merge
type_merge(first.type?, second.type?)
else
# combined_union_of
combined_union_of compact_types(nodes, &.type?)
end

combined_union_of compact_types(nodes, &.type?)
end

def type_merge_two(first, second)
if first == second
# Same, so return any of them
{true, first}
elsif first
if second
# first and second not nil and different
if first.opaque_id > second.opaque_id
first, second = second, first
end
def type_merge(first : Type?, second : Type?) : Type?
# Same, so return any of them
return first if first == second

if first.nil_type?
if second.is_a?(UnionType) && second.union_types.includes?(first)
return true, second
end
end
# First is nil, so return second
return second unless first

# puts "#{first} vs. #{second}"
{false, nil}
else
# Second is nil, so return first
{true, first}
end
else
# First is nil, so return second
{true, second}
# Second is nil, so return first
return first unless second

# NoReturn is removed from unions
return second if first.no_return?
return first if second.no_return?

# Check if a non-union type is part of a union type
if !first.is_a?(UnionType) && second.is_a?(UnionType) && second.union_types.includes?(first)
return second
end

if !second.is_a?(UnionType) && first.is_a?(UnionType) && first.union_types.includes?(second)
return first
end

# General case
combined_union_of compact_types({first, second})
end

def type_merge_union_of(types : Array(Type))
def type_merge_union_of(types : Array(Type)) : Type?
union_of compact_types(types)
end

def compact_types(types)
def compact_types(types) : Array(Type)
compact_types(types) { |type| type }
end

def compact_types(objects)
def compact_types(objects) : Array(Type)
all_types = Array(Type).new(objects.size)
objects.each { |obj| add_type all_types, yield(obj) }
all_types.reject! &.no_return? if all_types.size > 1
Expand Down Expand Up @@ -170,24 +162,24 @@ module Crystal
end

class Type
def self.merge(nodes : Array(ASTNode))
def self.merge(nodes : Array(ASTNode)) : Type?
nodes.find(&.type?).try &.type.program.type_merge(nodes)
end

def self.merge(types : Array(Type))
def self.merge(types : Array(Type)) : Type?
if types.size == 0
nil
else
types.first.program.type_merge(types)
end
end

def self.merge!(types_or_nodes)
def self.merge!(types_or_nodes) : Type
merge(types_or_nodes).not_nil!
end

def self.merge!(type1 : Type, type2 : Type)
merge!([type1, type2])
def self.merge!(type1 : Type, type2 : Type) : Type
type1.program.type_merge(type1, type2).not_nil!
end

# Given two non-union types T and U, returns their least common ancestor
Expand Down