Skip to content

Commit

Permalink
Merge branch 'release/0.18'
Browse files Browse the repository at this point in the history
Ary Borenszweig committed Jul 3, 2016

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 717c3cd + 6d21db7 commit ba44612
Showing 15 changed files with 97 additions and 146 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.18.7 (03-07-2016)

* The `compile` command was renamed back to `build`. The `compile` command is deprecated and will be removed in a future version
* Fibers now can be spawned with a name
* ECR macros can now be required with just `require "ecr"`
* [Several bugs fixes and enhancements](https://github.com/crystal-lang/crystal/issues?q=milestone%3A0.18.7+is%3Aclosed)

## 0.18.6 (28-06-2016)

* `T?` is now parsed as `Union(T, Nil)` outside the type grammar
18 changes: 0 additions & 18 deletions spec/compiler/codegen/class_var_spec.cr
Original file line number Diff line number Diff line change
@@ -179,24 +179,6 @@ describe "Codegen: class var" do
)).to_i.should eq(3)
end

it "initializes class var conditionally" do
run(%(
class Foo
if 1 == 2
@@x = 3
else
@@x = 4
end
def self.x
@@x
end
end
Foo.x
)).to_i.should eq(4)
end

it "codegens second class var initializer" do
run(%(
class Foo
24 changes: 23 additions & 1 deletion spec/compiler/type_inference/class_var_spec.cr
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ describe "Type inference: class var" do
Foo.x
),
"class variable '@@x' of Foo is read here before it was initialized, rendering it nilable, but its type is Int32"
"class variable '@@x' of Foo is not nilable (it's Int32) so it must have an initializer"
end
it "types class var" do
assert_type("
@@ -478,4 +478,26 @@ describe "Type inference: class var" do
Foo.foo
)) { nilable int32 }
end

it "types as nilable if doesn't have initializer" do
assert_type(%(
class Foo
def self.x
@@x = 1
@@x
end
end
Foo.x
)) { nilable int32 }
end

it "errors if class variable not nilable without initializer" do
assert_error %(
class Foo
@@foo : Int32
end
),
"class variable '@@foo' of Foo is not nilable (it's Int32) so it must have an initializer"
end
end
36 changes: 0 additions & 36 deletions spec/compiler/type_inference/const_spec.cr
Original file line number Diff line number Diff line change
@@ -162,16 +162,6 @@ describe "Type inference: const" do
)) { bool }
end

it "detects recursive constant definition" do
assert_error %(
A = B
B = A
A
),
"recursive dependency of constant A: A -> B -> A"
end

["nil", "true", "1", "'a'", %("foo"), "+ 1", "- 2", "~ 2", "1 + 2", "1 + Z"].each do |node|
it "doesn't errors if constant depends on another one defined later through method, but constant is simple (#{node})" do
infer_type(%(
@@ -207,19 +197,6 @@ describe "Type inference: const" do
)) { types["LibC"].types["Foo"] }
end

it "errors if constant depends on a global initialized later" do
assert_error %(
A = foo
$b = 1
def foo
$b
end
A
), "constant A requires initialization of $b, which is initialized later. Initialize $b before A"
end

it "doesn't error if constant depends on a global var that is never initialized" do
assert_type(%(
A = foo
@@ -256,19 +233,6 @@ describe "Type inference: const" do
"can't declare constant dynamically"
end

it "errors if recursive constant definition" do
assert_error %(
def foo(x)
end
foo B
B = A
A = B
),
"recursive dependency of constant B: B -> A -> B"
end

it "can use constant defined later (#2906)" do
assert_type(%(
FOO = Foo.new
11 changes: 0 additions & 11 deletions spec/compiler/type_inference/global_spec.cr
Original file line number Diff line number Diff line change
@@ -478,17 +478,6 @@ describe "Global inference" do
)) { int32 }
end

it "doesn't crash when trying to infer from a recursive constant definition" do
assert_error %(
A = B
B = A
$x = A
$x
),
"recursive dependency of constant A: A -> B -> A"
end

it "doesn't infer from redefined method" do
assert_type(%(
def foo
2 changes: 1 addition & 1 deletion src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ module Crystal
def type_top_level(sources : Array(Source))
program = new_program(sources)
node, original_node = parse program, sources
node = program.infer_type_top_level(node, @stats)
node, processor = program.infer_type_top_level(node, @stats)
Result.new program, node, original_node
end

8 changes: 0 additions & 8 deletions src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
@@ -40,10 +40,6 @@ module Crystal
# as the program starts, before the main code.
getter! class_var_and_const_initializers

# The list of class vars and const being typed, to check
# a recursive dependency.
getter! class_var_and_const_being_typed

getter! argc : Const
getter! argv : Const

@@ -64,7 +60,6 @@ module Crystal
@after_inference_types = Set(Type).new
@string_pool = StringPool.new
@class_var_and_const_initializers = [] of ClassVarInitializer | Const
@class_var_and_const_being_typed = [] of MetaTypeVar | Const
@tempfiles = [] of String

types = @types = {} of String => Type
@@ -156,7 +151,6 @@ module Crystal
types["Union"] = @union = GenericUnionType.new self, self, "Union", value, ["T"]

types["Crystal"] = crystal_module = NonGenericModuleType.new self, self, "Crystal"
crystal_module.locations << Location.new(__LINE__ - 1, 0, __FILE__)

argc_primitive = Primitive.new(:argc)
argc_primitive.type = int32
@@ -190,7 +184,6 @@ module Crystal

private def define_crystal_constants
types["Crystal"] = @crystal = crystal = NonGenericModuleType.new self, self, "Crystal"
crystal.locations << Location.new(__LINE__ - 1, 0, __FILE__)

version, sha = Crystal::Config.version_and_sha

@@ -218,7 +211,6 @@ module Crystal

private def define_crystal_constant(name, value)
crystal.types[name] = const = Const.new self, crystal, name, value
const.locations << Location.new(0, 0, __FILE__)
const.initialized = true
end

17 changes: 0 additions & 17 deletions src/compiler/crystal/semantic/base_type_visitor.cr
Original file line number Diff line number Diff line change
@@ -32,11 +32,6 @@ module Crystal
type = resolve_ident(node)
case type
when Const
existing = @mod.class_var_and_const_being_typed.find &.same?(type)
if existing
raise_recursive_dependency node, type
end

if !type.value.type? && !type.visited?
type.visited = true

@@ -46,14 +41,11 @@ module Crystal
type_visitor.types = type.scope_types
type_visitor.scope = type.scope

@mod.class_var_and_const_being_typed.push type
type.value.accept type_visitor
@mod.class_var_and_const_being_typed.pop

type.vars = const_def.vars
type.visitor = self
type.used = true
@mod.class_var_and_const_initializers << type
end

node.target_const = type
@@ -75,15 +67,6 @@ module Crystal
end
end

private def raise_recursive_dependency(node, const_or_class_var)
msg = mod.class_var_and_const_being_typed.join(" -> ") { |x| const_or_class_var_name(x) }
if const_or_class_var.is_a?(Const)
node.raise "recursive dependency of constant #{const_or_class_var}: #{msg} -> #{const_or_class_var_name(const_or_class_var)}"
else
node.raise "recursive dependency of class var #{const_or_class_var_name(const_or_class_var)}: #{msg} -> #{const_or_class_var_name(const_or_class_var)}"
end
end

private def const_or_class_var_name(const_or_class_var)
if const_or_class_var.is_a?(Const)
const_or_class_var.to_s
Original file line number Diff line number Diff line change
@@ -67,16 +67,7 @@ module Crystal
had_class_var = false
end

check_recursiveness = true
if class_var.uninitialized
check_recursiveness = false
elsif class_var.type?.try &.includes_type?(nil_type)
check_recursiveness = false
end

self.class_var_and_const_being_typed.push class_var if check_recursiveness
node.accept main_visitor
self.class_var_and_const_being_typed.pop if check_recursiveness

unless had_class_var
main_visitor.undefined_class_variable(class_var, owner)
21 changes: 1 addition & 20 deletions src/compiler/crystal/semantic/cleanup_transformer.cr
Original file line number Diff line number Diff line change
@@ -66,8 +66,6 @@ module Crystal
# idea on how to generate code for unreachable branches, because they have no type,
# and for now the codegen only deals with typed nodes.
class CleanupTransformer < Transformer
@const_being_initialized : Path?

def initialize(@program : Program)
@transformed = Set(UInt64).new
@def_nest_count = 0
@@ -211,11 +209,7 @@ module Crystal

if target.is_a?(Path)
const = target.target_const.not_nil!
if const.used
@const_being_initialized = target
else
return node
end
return node unless const.used
end

node.value = node.value.transform self
@@ -228,7 +222,6 @@ module Crystal
const = const.not_nil!
const.initialized = true
const.value = const.value.transform self
@const_being_initialized = nil
end

if target.is_a?(Global)
@@ -255,18 +248,6 @@ module Crystal
return expanded
end

if const_node = @const_being_initialized
const_being_initialized = const_node.target_const.not_nil!

if !@program.initialized_global_vars.includes?(node.name)
global_var = @program.global_vars[node.name]
if global_var.type?.try { |t| !t.includes_type?(@program.nil) }
const_node.raise "constant #{const_being_initialized} requires initialization of #{node}, \
which is initialized later. Initialize #{node} before #{const_being_initialized}"
end
end
end

node
end

21 changes: 0 additions & 21 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
@@ -429,20 +429,6 @@ module Crystal

def visit_class_var(node)
var = lookup_class_var(node)

existing = @mod.class_var_and_const_being_typed.find &.same?(var)
if existing
raise_recursive_dependency node, var
end

if !var.initializer && first_time_accessing_meta_type_var?(var)
var_type = var.type?
if var_type && !var_type.includes_type?(mod.nil)
node.raise "class variable '#{var.name}' of #{var.owner} is read here before it was initialized, rendering it nilable, but its type is #{var_type}"
end
var.bind_to mod.nil_var
end

node.bind_to var
node.var = var
var
@@ -642,13 +628,6 @@ module Crystal
var = lookup_class_var(target)
check_class_var_is_thread_local(target, var, attributes)

# If we are assigning to a class variable inside a method, make it nilable
# if this is the first time we are assigning to it, because
# the method might be called conditionally
if @typed_def && first_time_accessing_meta_type_var?(var)
var.bind_to mod.nil_var
end

target.bind_to var

node.bind_to value
40 changes: 37 additions & 3 deletions src/compiler/crystal/semantic/type_declaration_processor.cr
Original file line number Diff line number Diff line change
@@ -68,6 +68,9 @@ module Crystal
info : InitializeInfo,
name : String

private getter type_decl_visitor
private getter type_guess_visitor

def initialize(@program : Program)
# The type of instance variables. The last one wins.
#
@@ -106,11 +109,15 @@ module Crystal
# instance variables. These are gathered by the guesser, and later
# removed if an explicit type is found (in remove_error).
@errors = {} of Type => Hash(String, Error)

@type_decl_visitor = TypeDeclarationVisitor.new(@program, @explicit_instance_vars)

@type_guess_visitor = TypeGuessVisitor.new(@program, @explicit_instance_vars,
@guessed_instance_vars, @initialize_infos, @instance_vars_outside, @errors)
end

def process(node)
# First check type declarations
type_decl_visitor = TypeDeclarationVisitor.new(@program, @explicit_instance_vars)
node.accept type_decl_visitor

# Use the last type found for global variables to declare them
@@ -127,8 +134,6 @@ module Crystal

# Then use several syntactic rules to infer the types of
# variables that don't have an explicit type set
type_guess_visitor = TypeGuessVisitor.new(@program, @explicit_instance_vars,
@guessed_instance_vars, @initialize_infos, @instance_vars_outside, @errors)
node.accept type_guess_visitor

# Process global variables
@@ -171,6 +176,7 @@ module Crystal
var.type = type
var.bind_to(var)
var.freeze_type = type
var.location = location
vars[name] = var
var
end
@@ -549,6 +555,34 @@ module Crystal
end
end

def check_non_nilable_class_vars_without_initializers
type_decl_visitor.class_vars.each do |owner, vars|
vars.each_key do |name|
check_non_nilable_class_var_without_initializers(owner, name)
end
end

type_guess_visitor.class_vars.each do |owner, vars|
vars.each_key do |name|
check_non_nilable_class_var_without_initializers(owner, name)
end
end
end

private def check_non_nilable_class_var_without_initializers(owner, name)
class_var = owner.class_vars[name]?
return unless class_var

return if class_var.uninitialized

var_type = class_var.type?
return unless var_type

if !class_var.initializer && !var_type.includes_type?(@program.nil_type)
class_var.raise "class variable '#{name}' of #{owner} is not nilable (it's #{var_type}) so it must have an initializer"
end
end

private def remove_error(type, name)
@errors[type]?.try &.delete(name)
end
1 change: 1 addition & 0 deletions src/compiler/crystal/semantic/type_declaration_visitor.cr
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ module Crystal
def visit_type_declarations(node)
processor = TypeDeclarationProcessor.new(self)
processor.process(node)
{node, processor}
end
end

7 changes: 6 additions & 1 deletion src/compiler/crystal/semantic/type_inference.cr
Original file line number Diff line number Diff line change
@@ -23,11 +23,16 @@ module Crystal
# - main: process "main" code, calls and method bodies (the whole program).
# - check recursive structs (RecursiveStructChecker): check that structs are not recursive (impossible to codegen)
def infer_type(node, stats = false)
infer_type_top_level(node, stats: stats)
node, processor = infer_type_top_level(node, stats: stats)

Crystal.timing("Semantic (cvars initializers)", stats) do
visit_class_vars_initializers(node)
end

# Check that class vars without an initializer are nilable,
# give an error otherwise
processor.check_non_nilable_class_vars_without_initializers

Crystal.timing("Semantic (ivars initializers)", stats) do
visit_instance_vars_initializers(node)
end
21 changes: 21 additions & 0 deletions src/compiler/crystal/tools/doc/generator.cr
Original file line number Diff line number Diff line change
@@ -2,12 +2,14 @@ class Crystal::Doc::Generator
getter program : Program

@base_dir : String
@is_crystal_repo : Bool

def initialize(@program : Program, @included_dirs : Array(String), @dir = "./doc")
@base_dir = `pwd`.chomp
@types = {} of Crystal::Type => Doc::Type
@repo_name = ""
compute_repository
@is_crystal_repo = @repo_name == "github.com/crystal-lang/crystal"
end

def run
@@ -102,6 +104,7 @@ class Crystal::Doc::Generator

def must_include?(type : Crystal::Type)
return false if nodoc?(type)
return true if crystal_builtin?(type)

type.locations.any? do |type_location|
must_include? type_location
@@ -151,6 +154,24 @@ class Crystal::Doc::Generator
nodoc? obj.doc.try &.strip
end

def crystal_builtin?(type)
return false unless @is_crystal_repo
return false unless type.is_a?(Const) || type.is_a?(NonGenericModuleType)

crystal_type = @program.types["Crystal"]
return true if type == crystal_type

return false unless type.is_a?(Const)
return false unless type.container == crystal_type

{"BUILD_COMMIT", "BUILD_DATE", "CACHE_DIR", "DEFAULT_PATH",
"DESCRIPTION", "PATH", "VERSION"}.each do |name|
return true if type == crystal_type.types[name]?
end

false
end

def type(type)
@types[type] ||= Type.new(self, type)
end

0 comments on commit ba44612

Please sign in to comment.