Forum | Documentation | Website | Blog

Skip to content
Snippets Groups Projects
Commit 49854847 authored by Ary Borenszweig's avatar Ary Borenszweig
Browse files

The magic variables '$~' and '$?' are now method-local and concurrent-safe....

The magic variables '$~' and '$?' are now method-local and concurrent-safe. 'Tuple' is now correctly considered a struct. 'Pointer' is now correctly considered a struct. Renamed 'Function' to 'Proc'.
parent d4e17ebd
Branches
Tags BVF-0.5.0
No related merge requests found
Showing
with 293 additions and 50 deletions
## Next
## 0.5.10 (transitional) (2015-02-12)
* **Note**: This release makes core, breaking changes to the language, and doesn't work out of the box with its accompanying standard library. Use 0.6.0 instead.
* Improved error messages related to nilable instance variables.
* The magic variables `$~` and `$?` are now method-local and concurrent-safe.
* `Tuple` is now correctly considered a struct
* `Pointer` is now correctly considered a struct
* Renamed `Function` to `Proc`
## 0.5.9 (2015-02-07)
......
......@@ -19,6 +19,7 @@ require "reference"
require "value"
require "struct"
require "function"
require "proc"
# require "thread"
require "gc"
require "gc/null"
......
......@@ -143,7 +143,7 @@ describe "Code gen: module" do
end
end
class Pointer
struct Pointer
def each
yield value
end
......
......@@ -251,4 +251,17 @@ describe "Code gen: pointer" do
pointerof(FOO).value
").to_i.should eq(1)
end
it "passes pointer of pointer to method" do
run("
def foo(x)
x.value.value
end
p = Pointer(Pointer(Int32)).malloc(1_u64)
p.value = Pointer(Int32).malloc(1_u64)
p.value.value = 1
foo p
").to_i.should eq(1)
end
end
require "../../spec_helper"
describe "Codegen: special vars" do
["$~", "$?"].each do |name|
it "codegens #{name}" do
run(%(
class Object; def not_nil!; self; end; end
def foo(z)
#{name} = "hey"
end
foo(2)
#{name}
)).to_string.should eq("hey")
end
it "codegens #{name} with nilable (1)" do
run(%(
require "prelude"
def foo
if 1 == 2
#{name} = "foo"
end
end
foo
begin
#{name}
rescue ex
"ouch"
end
)).to_string.should eq("ouch")
end
it "codegens #{name} with nilable (2)" do
run(%(
require "prelude"
def foo
if 1 == 1
#{name} = "foo"
end
end
foo
begin
#{name}
rescue ex
"ouch"
end
)).to_string.should eq("foo")
end
end
it "codegens $~ two levels" do
run(%(
class Object; def not_nil!; self; end; end
def foo
$? = "hey"
end
def bar
$? = foo
$?
end
bar
$?
)).to_string.should eq("hey")
end
end
......@@ -3,7 +3,7 @@ require "../../spec_helper"
describe "Code gen: splat" do
it "splats" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -17,7 +17,7 @@ describe "Code gen: splat" do
it "splats with another arg" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -31,7 +31,7 @@ describe "Code gen: splat" do
it "splats with two other args" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -56,7 +56,7 @@ describe "Code gen: splat" do
it "splats without args" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -70,7 +70,7 @@ describe "Code gen: splat" do
it "splats with default value" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -84,7 +84,7 @@ describe "Code gen: splat" do
it "splats with default value (2)" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......@@ -98,7 +98,7 @@ describe "Code gen: splat" do
it "splats with default value (3)" do
run(%(
class Tuple
struct Tuple
def length; {{@length}}; end
end
......
......@@ -25,7 +25,7 @@ describe "Code gen: tuple" do
it "accesses a tuple type and creates instance from it" do
run("
class Tuple
struct Tuple
def types
T
end
......@@ -48,7 +48,7 @@ describe "Code gen: tuple" do
it "allows malloc pointer of tuple" do
run("
class Pointer
struct Pointer
def self.malloc(size : Int)
malloc(size.to_u64)
end
......@@ -101,7 +101,7 @@ describe "Code gen: tuple" do
it "gets length at compile time" do
run(%(
class Tuple
struct Tuple
def my_length
{{ @length }}
end
......
......@@ -690,17 +690,22 @@ describe "Parser" do
it_parses "foo $a", Call.new(nil, "foo", Global.new("$a"))
it_parses "$~", Call.new(Path.global("MatchData"), "last")
it_parses "$1", Call.new(Call.new(Path.global("MatchData"), "last"), "[]", 1.int32)
it_parses "foo $1", Call.new(nil, "foo", Call.new(Call.new(Path.global("MatchData"), "last"), "[]", 1.int32))
it_parses "$~", Call.new("$~".var, "not_nil!")
it_parses "$~.foo", Call.new(Call.new("$~".var, "not_nil!"), "foo")
it_parses "$1", Call.new(Call.new("$~".var, "not_nil!"), "[]", 1.int32)
it_parses "foo $1", Call.new(nil, "foo", Call.new(Call.new("$~".var, "not_nil!"), "[]", 1.int32))
it_parses "$~ = 1", Assign.new("$~".var, 1.int32)
it_parses "foo /a/", Call.new(nil, "foo", regex("a"))
it_parses "foo(/a/)", Call.new(nil, "foo", regex("a"))
it_parses "foo(/ /)", Call.new(nil, "foo", regex(" "))
it_parses "foo(/ /, / /)", Call.new(nil, "foo", [regex(" "), regex(" ")] of ASTNode)
it_parses "foo a, / /", Call.new(nil, "foo", ["a".call, regex(" ")] of ASTNode)
it_parses "$?", Call.new(Path.global(["Process", "Status"]), "last")
it_parses "foo $?", Call.new(nil, "foo", Call.new(Path.global(["Process", "Status"]), "last"))
it_parses "$?", Call.new("$?".var, "not_nil!")
it_parses "$?.foo", Call.new(Call.new("$?".var, "not_nil!"), "foo")
it_parses "foo $?", Call.new(nil, "foo", Call.new("$?".var, "not_nil!"))
it_parses "$? = 1", Assign.new("$?".var, 1.int32)
it_parses "$0", Path.global("PROGRAM_NAME")
it_parses "foo $0", Call.new(nil, "foo", Path.global("PROGRAM_NAME"))
......
......@@ -403,15 +403,15 @@ describe "Type inference: fun" do
)) { float64 }
end
it "allows writing a function type with Function" do
it "allows writing a function type with Proc" do
assert_type(%(
Function(Int32, Int32)
Proc(Int32, Int32)
)) { fun_of(int32, int32).metaclass }
end
it "allows using Function as restriction (1)" do
it "allows using Proc as restriction (1)" do
assert_type(%(
def foo(x : Function(Int32, Int32))
def foo(x : Proc(Int32, Int32))
x.call(2)
end
......@@ -419,9 +419,9 @@ describe "Type inference: fun" do
)) { int32 }
end
it "allows using Function as restriction (2)" do
it "allows using Proc as restriction (2)" do
assert_type(%(
def foo(x : Function)
def foo(x : Proc)
x.call(2)
end
......@@ -429,9 +429,9 @@ describe "Type inference: fun" do
)) { int32 }
end
it "allows using Function as restriction (3)" do
it "allows using Proc as restriction (3)" do
assert_type(%(
def foo(x : Function(T, U))
def foo(x : Proc(T, U))
T
end
......
require "../../spec_helper"
describe "Type inference: special vars" do
["$~", "$?"].each do |name|
it "infers #{name}" do
assert_type(%(
class Object; def not_nil!; self; end; end
def foo
#{name} = "hey"
end
foo
#{name}
)) { nilable string }
end
it "errors if #{name} is not defined" do
assert_error %(
#{name}
),
"'#{name}' was not defined by any previous call"
end
it "errors if #{name} is not defined (2)" do
assert_error %(
class Object; def not_nil!; self; end; end
def foo
#{name} = "hey"
#{name}
end
foo
),
"'#{name}' was not defined by any previous call"
end
it "errors if #{name} is not a reference nilable type" do
assert_error %(
class Object; def not_nil!; self; end; end
def foo
#{name} = 1
end
foo
#{name}
),
"'#{name}' only allows reference nilable types"
end
it "errors if assigning #{name} at top level" do
assert_error %(
#{name} = "hey"
),
"'#{name}' can't be assigned at the top level"
end
end
end
......@@ -32,7 +32,7 @@ describe "Type inference: tuples" do
it "types T as a tuple of metalcasses" do
assert_type("
class Tuple
struct Tuple
def types
T
end
......
require "spec"
describe "Function" do
describe "Proc" do
it "does to_s(io)" do
str = StringIO.new
f = ->(x : Int32) { x.to_f }
......@@ -47,11 +47,10 @@ describe "Function" do
f.closure?.should be_true
end
# TOOD: uncomment in 0.5.9
# it "does new" do
# a = 1
# f = ->(x : Int32){ x + a }
# f2 = Function(Int32, Int32).new(f.pointer, f.closure_data)
# f2.call(3).should eq(4)
# end
it "does new" do
a = 1
f = ->(x : Int32){ x + a }
f2 = Proc(Int32, Int32).new(f.pointer, f.closure_data)
f2.call(3).should eq(4)
end
end
......@@ -109,6 +109,11 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
call_args << call_arg
end
# Then special variables ($~, $?)
target_def.special_vars.try &.each do |special_var_name|
call_args << context.vars[special_var_name].pointer
end
# Then magic constants (__LINE__, __FILE__, __DIR__)
node.args.length.upto(target_def.args.length - 1) do |index|
arg = target_def.args[index]
......
......@@ -741,7 +741,9 @@ module Crystal
def visit(node : Var)
var = context.vars[node.name]?
if var
@last = downcast var.pointer, node.type, var.type, var.already_loaded
# Special variables always have an extra pointer
already_loaded = (node.special_var? ? false : var.already_loaded)
@last = downcast var.pointer, node.type, var.type, already_loaded
elsif node.name == "self"
if node.type.metaclass?
@last = type_id(node.type)
......
......@@ -133,6 +133,10 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
return args
end
target_def.special_vars.try &.each do |special_var_name|
args.push Arg.new(special_var_name, type: target_def.vars.not_nil![special_var_name].type)
end
if is_external
llvm_args_types = args.map { |arg| llvm_c_type(arg.type) }
llvm_return_type = llvm_c_return_type(target_def.type)
......@@ -143,7 +147,11 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
target_def.sret = true
end
else
llvm_args_types = args.map { |arg| llvm_arg_type(arg.type) }
llvm_args_types = args.map do |arg|
arg_type = llvm_arg_type(arg.type)
arg_type = arg_type.pointer if arg.special_var?
arg_type
end
llvm_return_type = llvm_type(target_def.type)
end
......@@ -266,7 +274,7 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
# We don't need to create a copy of the argument if it's never
# assigned a value inside the function.
needs_copy = target_def_var.try &.assigned_to
if needs_copy
if needs_copy && !arg.special_var?
pointer = alloca(llvm_type(var_type), arg.name)
context.vars[arg.name] = LLVMVar.new(pointer, var_type)
else
......
......@@ -205,6 +205,10 @@ module Crystal
llvm_type type
end
def create_llvm_struct_type(type : TupleInstanceType)
llvm_type type
end
def create_llvm_struct_type(type : CStructType)
LLVM::Type.struct(type.llvm_name, type.packed) do |a_struct|
@struct_cache[type] = a_struct
......@@ -287,6 +291,10 @@ module Crystal
llvm_type type
end
def llvm_embedded_type(type : PointerInstanceType)
llvm_type type
end
def llvm_embedded_type(type : InstanceVarContainer)
if type.struct?
llvm_struct_type type
......
......@@ -66,7 +66,9 @@ module Crystal
@types["Float64"] = @float64 = FloatType.new self, self, "Float64", float, 8, 10
@types["Symbol"] = @symbol = SymbolType.new self, self, "Symbol", value, 4
@types["Pointer"] = @pointer = PointerType.new self, self, "Pointer", value, ["T"]
@types["Pointer"] = pointer = @pointer = PointerType.new self, self, "Pointer", value, ["T"]
pointer.struct = true
@types["Tuple"] = @tuple = TupleType.new self, self, "Tuple", value, ["T"]
@types["StaticArray"] = static_array = @static_array = StaticArrayType.new self, self, "StaticArray", value, ["T", "N"]
......@@ -102,8 +104,8 @@ module Crystal
enum_t.abstract = true
enum_t.struct = true
@types["Function"] = function = @function = FunType.new self, self, "Function", value, ["T"]
function.variadic = true
@types["Proc"] = proc = @proc = FunType.new self, self, "Proc", value, ["T"]
proc.variadic = true
@types["ARGC_UNSAFE"] = argc_unsafe = Const.new self, self, "ARGC_UNSAFE", Primitive.new(:argc)
@types["ARGV_UNSAFE"] = argv_unsafe = Const.new self, self, "ARGV_UNSAFE", Primitive.new(:argv)
......@@ -215,7 +217,7 @@ module Crystal
end
def fun_of(types : Array)
function.instantiate(types)
proc.instantiate(types)
end
def add_to_requires(filename)
......@@ -246,7 +248,7 @@ module Crystal
{% for name in %w(object no_return value number reference void nil bool char int int8 int16 int32 int64
uint8 uint16 uint32 uint64 float float32 float64 string symbol pointer array static_array
exception tuple function enum) %}
exception tuple proc enum) %}
def {{name.id}}
@{{name.id}}.not_nil!
end
......
......@@ -167,8 +167,8 @@ module Crystal
property :previous
property :next
property :visibility
getter :special_vars
def macro_owner=(@macro_owner)
end
......@@ -176,6 +176,11 @@ module Crystal
def macro_owner
@macro_owner || @owner
end
def add_special_var(name)
special_vars = @special_vars ||= Set(String).new
special_vars << name
end
end
class PointerOf
......@@ -498,4 +503,16 @@ module Crystal
def initialize(@name, @reason, @nodes = nil)
end
end
class Arg
def special_var?
@name.starts_with? '$'
end
end
class Var
def special_var?
@name.starts_with? '$'
end
end
end
......@@ -79,8 +79,17 @@ class Crystal::Call
bind_to matches if matches
bind_to block.break if block
if (parent_visitor = @parent_visitor) && parent_visitor.typed_def? && matches && matches.any?(&.raises)
parent_visitor.typed_def.raises = true
if (parent_visitor = @parent_visitor) && matches
if parent_visitor.typed_def? && matches.any?(&.raises)
parent_visitor.typed_def.raises = true
end
matches.each do |match|
match.special_vars.try &.each do |special_var_name|
special_var = match.vars.not_nil![special_var_name]
parent_visitor.define_special_var(special_var_name, special_var)
end
end
end
end
......
......@@ -68,6 +68,7 @@ module Crystal
@is_initialize = !!(typed_def && typed_def.name == "initialize")
@found_self_in_initialize_call = nil
@used_ivars_in_calls_in_initialize = nil
@has_special_vars_defined = false
@in_type_args = 0
@attributes = nil
@lib_def_pass = 0
......@@ -129,6 +130,10 @@ module Crystal
def visit(node : Var)
var = @vars[node.name]?
if var
if !@has_special_vars_defined && node.special_var?
node.raise "'#{node.name}' was not defined by any previous call"
end
meta_var = @meta_vars[node.name]
check_closured meta_var
......@@ -153,6 +158,8 @@ module Crystal
else
node.type = current_type.metaclass
end
elsif node.special_var?
node.raise "'#{node.name}' was not defined by any previous call"
else
node.raise "read before definition of '#{node.name}'"
end
......@@ -341,16 +348,16 @@ module Crystal
end
def type_assign(target : Var, value, node)
var_name = target.name
value.accept self
value_type_filters = @type_filters
@type_filters = nil
target.bind_to value
node.bind_to value
var_name = target.name
value_type_filters = @type_filters
@type_filters = nil
meta_var = (@meta_vars[var_name] ||= new_meta_var(var_name))
meta_var.bind_to value
meta_var.assigned_to = true
......@@ -375,6 +382,17 @@ module Crystal
if needs_type_filters?
@type_filters = TypeFilters.and(TypeFilters.truthy(target), value_type_filters)
end
if target.special_var?
if typed_def = @typed_def
typed_def.add_special_var(target.name)
else
node.raise "'#{var_name}' can't be assigned at the top level"
end
end
end
def type_assign_helper(var_name, target, value)
end
def type_assign(target : InstanceVar, value, node)
......@@ -3187,6 +3205,21 @@ module Crystal
end
end
def define_special_var(name, value)
meta_var = (@meta_vars[name] ||= new_meta_var(name))
meta_var.bind_to value
meta_var.bind_to mod.nil_var unless meta_var.dependencies.any? &.same?(mod.nil_var)
meta_var.assigned_to = true
check_closured meta_var
unless meta_var.type.is_a?(NilableType)
value.raise "'#{name}' only allows reference nilable types, not #{meta_var.type}"
end
@vars[name] = meta_var
@has_special_vars_defined = true
end
def new_meta_var(name, context = current_context)
meta_var = MetaVar.new(name)
meta_var.context = context
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment