Skip to content

Commit

Permalink
Added: private def at the top-level of a file is only available ins…
Browse files Browse the repository at this point in the history
…ide that file
  • Loading branch information
Ary Borenszweig committed Jan 2, 2015
1 parent de1d35f commit 79805a5
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 2 deletions.
23 changes: 23 additions & 0 deletions spec/compiler/codegen/private_def_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require "../../spec_helper"

describe "Codegen: private def" do
it "codegens private def in same file" do
compiler = Compiler.new
sources = [
Compiler::Source.new("foo.cr", %(
private def foo
1
end
foo
)),
]
compiler.prelude = "empty"

tempfile = Tempfile.new("crystal-spec-output")
output_filename = tempfile.path
tempfile.close

compiler.compile sources, output_filename
end
end
72 changes: 72 additions & 0 deletions spec/compiler/type_inference/private_def_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "../../spec_helper"

describe "Type inference: private def" do
it "doesn't find private def in another file" do
expect_raises Crystal::TypeException, "undefined local variable or method 'foo'" do
compiler = Compiler.new
sources = [
Compiler::Source.new("foo.cr", %(
private def foo
1
end
)),
Compiler::Source.new("bar.cr", %(
foo
)),
]
compiler.no_build = true
compiler.prelude = "empty"
compiler.compile sources, "output"
end
end

it "finds private def in same file" do
compiler = Compiler.new
sources = [
Compiler::Source.new("foo.cr", %(
private def foo
1
end
foo
)),
]
compiler.no_build = true
compiler.prelude = "empty"
compiler.compile sources, "output"
end

it "finds private def in same file that invokes another def" do
compiler = Compiler.new
sources = [
Compiler::Source.new("foo.cr", %(
def bar
2
end
private def foo
bar
end
foo
)),
]
compiler.no_build = true
compiler.prelude = "empty"
compiler.compile sources, "output"
end

it "types private def correctly" do
assert_type(%(
private def foo
1
end
def foo
'a'
end
foo
)) { int32 }
end
end
18 changes: 18 additions & 0 deletions src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,28 @@ module Crystal
@def_macros = [] of Def
@splat_expansions = {} of Def => Type
@initialized_global_vars = Set(String).new
@file_modules = {} of String => FileModule

define_primitives
end

def add_def(node : Def)
return super unless node.visibility == :private

location = node.location
return super unless location

filename = location.filename
return super unless filename.is_a?(String)

file_module = @file_modules[filename] ||= FileModule.new(self, self, filename)
file_module.add_def node
end

def lookup_private_matches(filename, signature)
@file_modules[filename]?.try &.lookup_matches(signature)
end

setter target_machine

def target_machine
Expand Down
29 changes: 27 additions & 2 deletions src/compiler/crystal/semantic/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ class Crystal::Call
lookup_matches_in_type(owner, arg_types, self_type, def_name)
end

def lookup_matches_in(owner : FileModule, arg_types)
lookup_matches_in mod, arg_types
end

def lookup_matches_in(owner : NonGenericModuleType, arg_types)
including_types = owner.including_types
if including_types
Expand Down Expand Up @@ -176,7 +180,7 @@ class Crystal::Call
matches = bubbling_exception { parent_visitor.typed_def.original_owner.lookup_matches signature }
matches.each &.context.owner = owner
else
matches = bubbling_exception { owner.lookup_matches signature }
matches = bubbling_exception { lookup_matches_with_signature(owner, signature) }
end
end

Expand All @@ -201,7 +205,7 @@ class Crystal::Call
matches = Matches.new([Match.new(initialize_def, arg_types, MatchContext.new(owner, owner))], true)
end
elsif !obj && owner != mod
mod_matches = mod.lookup_matches(signature)
mod_matches = lookup_matches_with_signature(mod, signature)
matches = mod_matches unless mod_matches.empty?
end
end
Expand Down Expand Up @@ -240,6 +244,27 @@ class Crystal::Call
raise "Bug: trying to lookup matches in nil in #{self}"
end

def lookup_matches_with_signature(owner : Program, signature)
location = self.location
if location && (filename = location.filename).is_a?(String)
matches = owner.lookup_private_matches filename, signature
end

if matches
if matches.empty?
matches = owner.lookup_matches signature
end
else
matches = owner.lookup_matches signature
end

matches
end

def lookup_matches_with_signature(owner, signature)
owner.lookup_matches signature
end

def instantiate(matches, owner, self_type = nil)
block = @block

Expand Down
7 changes: 7 additions & 0 deletions src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,13 @@ module Crystal
end
end

# A module that is related to a file and contains its private defs.
class FileModule < NonGenericModuleType
def passed_as_self?
false
end
end

abstract class ClassType < ModuleType
include InheritableClass

Expand Down

0 comments on commit 79805a5

Please sign in to comment.