Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ea19ae23e016
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a2ff2039df41
Choose a head ref
  • 3 commits
  • 7 files changed
  • 1 contributor

Commits on Jul 29, 2016

  1. Compiler: try to invoke finalize in new if a type responds to it. F…

    …ixes #3060
    Ary Borenszweig committed Jul 29, 2016

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6967838 View commit details
  2. Fixed #3045: error when redining fun with different signautre

    Ary Borenszweig committed Jul 29, 2016
    Copy the full SHA
    c2ba050 View commit details
  3. Compiler: no need to store "new expansions" in Program

    Ary Borenszweig committed Jul 29, 2016
    Copy the full SHA
    a2ff203 View commit details
23 changes: 23 additions & 0 deletions spec/compiler/semantic/lib_spec.cr
Original file line number Diff line number Diff line change
@@ -850,4 +850,27 @@ describe "Semantic: lib" do
LibFoo::Foo.new.x
)) { int32 }
end

it "errors if defining incompatible funs with the same name in the same lib (#3045)" do
assert_error %(
lib LibFoo
fun foo1 = foo
fun foo2 = foo(x : Int32)
end
),
"fun redefinition with different signature"
end

it "errors if defining incompatible funs with the same name in different libs (#3045)" do
assert_error %(
lib LibFoo1
fun foo1 = foo
end
lib LibFoo2
fun foo2 = foo(x : Int32)
end
),
"fun redefinition with different signature"
end
end
8 changes: 5 additions & 3 deletions src/compiler/crystal/semantic.cr
Original file line number Diff line number Diff line change
@@ -51,11 +51,13 @@ class Crystal::Program
# This alone is useful for some tools like doc or hierarchy
# where a full semantic of the program is not needed.
def top_level_semantic(node, stats = false)
Crystal.timing("Semantic (top level)", stats) do
node.accept TopLevelVisitor.new(self)
new_expansions = Crystal.timing("Semantic (top level)", stats) do
visitor = TopLevelVisitor.new(self)
node.accept visitor
visitor.new_expansions
end
Crystal.timing("Semantic (new)", stats) do
define_new_methods
define_new_methods(new_expansions)
end
node, processor = Crystal.timing("Semantic (type declarations)", stats) do
TypeDeclarationProcessor.new(self).process(node)
29 changes: 12 additions & 17 deletions src/compiler/crystal/semantic/new.cr
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
module Crystal
class Program
# This is a recording of a `new` method (expanded) that
# was created from an `initialize` method (original)
record NewExpansion, original : Def, expanded : Def

@new_expansions = [] of NewExpansion
getter new_expansions

def define_new_methods
def define_new_methods(new_expansions)
# Here we complete the body of `self.new` methods
# created from `initialize` methods.
@new_expansions.each do |expansion|
expansion.expanded.fill_body_from_initialize(expansion.original.owner)
new_expansions.each do |expansion|
expansion[:expanded].fill_body_from_initialize(expansion[:original].owner)
end

# We also need to define empty `new` methods for types
# that don't have any `initialize` methods.
define_default_new(@program)
define_default_new(self)
end

def define_default_new(type)
@@ -174,8 +167,8 @@ module Crystal
new_vars << DoubleSplat.new(Var.new(double_splat.name))
end

assign = Assign.new(obj, alloc)
init = Call.new(obj, "initialize", new_vars, named_args: named_args)
assign = Assign.new(obj.clone, alloc)
init = Call.new(obj.clone, "initialize", new_vars, named_args: named_args)

# If the initialize yields, call it with a block
# that yields those arguments.
@@ -188,7 +181,8 @@ module Crystal
exps = Array(ASTNode).new(4)
exps << assign
exps << init
exps << Call.new(Path.global("GC"), "add_finalizer", obj) if instance_type.has_finalizer?
exps << If.new(RespondsTo.new(obj.clone, "finalize"),
Call.new(Path.global("GC"), "add_finalizer", obj.clone))
exps << obj

# Forward block argument if any
@@ -205,7 +199,7 @@ module Crystal
#
# def new
# x = allocate
# GC.add_finalizer x
# GC.add_finalizer x if x.responds_to? :finalize
# x
# end
var = Var.new("x")
@@ -214,8 +208,9 @@ module Crystal

exps = Array(ASTNode).new(3)
exps << assign
exps << Call.new(Path.global("GC"), "add_finalizer", var) if instance_type.has_finalizer?
exps << var
exps << If.new(RespondsTo.new(var.clone, "finalize"),
Call.new(Path.global("GC"), "add_finalizer", var.clone))
exps << var.clone

Def.new("new", body: exps)
end
5 changes: 4 additions & 1 deletion src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
@@ -35,6 +35,9 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
ValidStructDefAttributes = %w(Packed)
ValidEnumDefAttributes = %w(Flags)

# These are `new` methods (expanded) that was created from `initialize` methods (original)
getter new_expansions = [] of {original: Def, expanded: Def}

@last_doc : String?

def visit(node : ClassDef)
@@ -277,7 +280,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
target_type.metaclass.add_def(new_method)

# And we register it to later complete it
@program.new_expansions << Program::NewExpansion.new(node, new_method)
new_expansions << {original: node, expanded: new_method}
end

run_hooks target_type.metaclass, target_type, :method_added, node, Call.new(nil, "method_added", [node] of ASTNode).at(node.location)
26 changes: 20 additions & 6 deletions src/compiler/crystal/semantic/type_declaration_visitor.cr
Original file line number Diff line number Diff line change
@@ -40,6 +40,10 @@ class Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor
# The type of class variables. The last one wins.
# This is type => variables.
@class_vars = {} of ClassVarContainer => Hash(String, TypeDeclarationWithLocation)

# A hash of all defined funs, so we can detect when
# a fun is redefined with a different signautre
@externals = {} of String => External
end

def visit(node : Alias)
@@ -131,13 +135,10 @@ class Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor

external.set_type(return_type)

begin
old_external = current_type.add_def external
rescue ex : Crystal::Exception
node.raise ex.message
end
old_external = add_external external
old_external.dead = true if old_external

old_external.dead = true if old_external.is_a?(External)
current_type.add_def(external)

if current_type.is_a?(Program)
key = DefInstanceKey.new external.object_id, external.args.map(&.type), nil, nil
@@ -169,6 +170,19 @@ class Crystal::TypeDeclarationVisitor < Crystal::SemanticVisitor
false
end

def add_external(external : External)
existing = @externals[external.real_name]?
if existing
if existing.compatible_with?(external)
return existing
else
external.raise "fun redefinition with different signature (was `#{existing}` at #{existing.location})"
end
end
@externals[external.real_name] = external
nil
end

def declare_c_struct_or_union_field(node)
type = current_type.as(NonGenericClassType)

26 changes: 0 additions & 26 deletions src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
@@ -462,14 +462,6 @@ module Crystal
0
end

def has_finalizer?
return false if struct?

signature = CallSignature.new "finalize", ([] of Type), nil, nil
matches = lookup_matches(signature)
!matches.empty?
end

def inspect(io)
to_s(io)
end
@@ -623,10 +615,6 @@ module Crystal
getter hooks : Array(Hook)?

def add_def(a_def)
if a_def.is_a?(External)
check_fun_redefinition(a_def)
end

a_def.owner = self

if a_def.visibility.public? && a_def.name == "initialize"
@@ -696,20 +684,6 @@ module Crystal
hooks << Hook.new(kind, a_def)
end

private def check_fun_redefinition(a_def)
if defs = self.defs
if existing_defs = defs[a_def.name]?
existing = existing_defs.first?
if existing
existing = existing.def.as(External)
unless existing.compatible_with?(a_def)
a_def.raise "fun redefinition with different signature (was #{existing})"
end
end
end
end
end

def filter_by_responds_to(name)
has_def?(name) ? self : nil
end
11 changes: 10 additions & 1 deletion src/gc/boehm.cr
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
{% else %}
@[Link("gc")]
{% end %}

lib LibGC
alias Int = LibC::Int
alias SizeT = LibC::SizeT
@@ -89,7 +90,15 @@ module GC
LibGC.free(pointer)
end

def self.add_finalizer(object : T)
def self.add_finalizer(object : Reference)
add_finalizer_impl(object)
end

def self.add_finalizer(object)
# Nothing
end

private def self.add_finalizer_impl(object : T)
LibGC.register_finalizer_ignore_self(object.as(Void*),
->(obj, data) { obj.as(T).finalize },
nil, nil, nil)