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: 69d731bfa73f
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: 8a01ad8c507b
Choose a head ref
  • 4 commits
  • 36 files changed
  • 1 contributor

Commits on Jul 28, 2016

  1. Compiler: more refactors

    - Split path lookup and type lookup
    - Rename type_lookup to path_lookup where appropriate
    - Optimized path lookup: no need to keep a set of analyzed types
    - Optimized type lookup: no need to create a class instance
    - Document path and type lookup
    Ary Borenszweig committed Jul 28, 2016
    Copy the full SHA
    0c62997 View commit details
  2. Compiler: renamed container to namespace in Type

    Ary Borenszweig committed Jul 28, 2016
    Copy the full SHA
    11bbf9c View commit details
  3. Compiler: removed unused in_const_block in codegen

    Ary Borenszweig committed Jul 28, 2016
    Copy the full SHA
    c657759 View commit details

Commits on Jul 29, 2016

  1. Compiler: more refactors, renames and docs

    Ary Borenszweig committed Jul 29, 2016
    Copy the full SHA
    8a01ad8 View commit details
Showing with 1,324 additions and 1,184 deletions.
  1. +1 −1 spec/compiler/crystal/types_spec.cr
  2. +1 −1 spec/compiler/semantic/class_spec.cr
  3. +2 −2 spec/compiler/semantic/visibility_modifiers_spec.cr
  4. +3 −0 src/compiler/crystal.cr
  5. +4 −30 src/compiler/crystal/codegen/codegen.cr
  6. +2 −2 src/compiler/crystal/codegen/const.cr
  7. +2 −1 src/compiler/crystal/command/docs.cr
  8. +9 −0 src/compiler/crystal/formatter.cr
  9. +19 −19 src/compiler/crystal/macros/macros.cr
  10. +44 −55 src/compiler/crystal/semantic.cr
  11. +2 −2 src/compiler/crystal/semantic/abstract_def_checker.cr
  12. +0 −23 src/compiler/crystal/semantic/ast.cr
  13. +21 −38 src/compiler/crystal/semantic/call.cr
  14. +8 −8 src/compiler/crystal/semantic/call_error.cr
  15. +4 −4 src/compiler/crystal/semantic/cleanup_transformer.cr
  16. +117 −0 src/compiler/crystal/semantic/enum.cr
  17. +1 −0 src/compiler/crystal/semantic/flags.cr
  18. +99 −0 src/compiler/crystal/semantic/hooks.cr
  19. +7 −4 src/compiler/crystal/semantic/main_visitor.cr
  20. +82 −5 src/compiler/crystal/semantic/match.cr
  21. +31 −21 src/compiler/crystal/semantic/method_lookup.cr
  22. +182 −0 src/compiler/crystal/semantic/path_lookup.cr
  23. +28 −29 src/compiler/crystal/semantic/restrictions.cr
  24. +418 −535 src/compiler/crystal/semantic/semantic_visitor.cr
  25. +12 −18 src/compiler/crystal/semantic/suggestions.cr
  26. +9 −4 src/compiler/crystal/semantic/top_level_visitor.cr
  27. +2 −0 src/compiler/crystal/semantic/type_declaration_visitor.cr
  28. +4 −4 src/compiler/crystal/semantic/type_guess_visitor.cr
  29. +124 −279 src/compiler/crystal/semantic/type_lookup.cr
  30. +9 −0 src/compiler/crystal/syntax.cr
  31. +0 −8 src/compiler/crystal/tools/doc.cr
  32. +1 −1 src/compiler/crystal/tools/doc/generator.cr
  33. +4 −2 src/compiler/crystal/tools/doc/markdown_doc_renderer.cr
  34. +23 −23 src/compiler/crystal/tools/doc/type.cr
  35. +40 −65 src/compiler/crystal/types.cr
  36. +9 −0 src/compiler/crystal/util.cr
2 changes: 1 addition & 1 deletion spec/compiler/crystal/types_spec.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "../../spec_helper"

def assert_type_to_s(expected)
private def assert_type_to_s(expected)
p = Program.new
t = with p yield p
t.to_s.should eq(expected)
2 changes: 1 addition & 1 deletion spec/compiler/semantic/class_spec.cr
Original file line number Diff line number Diff line change
@@ -353,7 +353,7 @@ describe "Semantic: class" do
mod.types["Foo"].types["Bar"].as(NonGenericClassType)
end

it "doesn't lookup type in parents' containers, and lookups and in program" do
it "doesn't lookup type in parents' namespaces, and lookups and in program" do
code = "
class Bar
end
4 changes: 2 additions & 2 deletions spec/compiler/semantic/visibility_modifiers_spec.cr
Original file line number Diff line number Diff line change
@@ -235,7 +235,7 @@ describe "Visibility modifiers" do
)) { int32 }
end

it "allows invoking protected method from container to contained" do
it "allows invoking protected method from namespace to namespaced type" do
assert_type(%(
class Foo
def foo
@@ -253,7 +253,7 @@ describe "Visibility modifiers" do
)) { int32 }
end

it "allows invoking protected method from contained to container" do
it "allows invoking protected method from namespaced type to namespace" do
assert_type(%(
class Foo
protected def foo
3 changes: 3 additions & 0 deletions src/compiler/crystal.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# This is the file that is compiled to generate the
# executable for the compiler.

require "./crystal/**"

Crystal::Command.run
34 changes: 4 additions & 30 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
@@ -580,7 +580,7 @@ module Crystal
end

def visit(node : ClassDef)
node.runtime_initializers.try &.each &.accept self
node.hook_expansions.try &.each &.accept self
accept node.body
@last = llvm_nil
false
@@ -647,14 +647,14 @@ module Crystal
end

def visit(node : Include)
node.runtime_initializers.try &.each &.accept self
node.hook_expansions.try &.each &.accept self

@last = llvm_nil
false
end

def visit(node : Extend)
node.runtime_initializers.try &.each &.accept self
node.hook_expansions.try &.each &.accept self

@last = llvm_nil
false
@@ -1231,7 +1231,7 @@ module Crystal
end

def visit(node : Def)
node.runtime_initializers.try &.each &.accept self
node.hook_expansions.try &.each &.accept self

@last = llvm_nil
false
@@ -1786,32 +1786,6 @@ module Crystal
value
end

def in_const_block(container)
old_llvm_mod = @llvm_mod
@llvm_mod = @main_mod

old_ensure_exception_handlers = @ensure_exception_handlers
old_rescue_block = @rescue_block
@ensure_exception_handlers = nil
@rescue_block = nil

with_cloned_context do
context.fun = @main

# "self" in a constant is the constant's container
context.type = container

# Start with fresh variables
context.vars = LLVMVars.new

yield
end

@llvm_mod = old_llvm_mod
@ensure_exception_handlers = old_ensure_exception_handlers
@rescue_block = old_rescue_block
end

def printf(format, args = [] of LLVM::Value)
call @program.printf(@llvm_mod), [builder.global_string_pointer(format)] + args
end
4 changes: 2 additions & 2 deletions src/compiler/crystal/codegen/const.cr
Original file line number Diff line number Diff line change
@@ -117,8 +117,8 @@ class Crystal::CodeGenVisitor

define_main_function(fun_name, ([] of LLVM::Type), LLVM::Void, needs_alloca: true) do |func|
with_cloned_context do
# "self" in a constant is the constant's container
context.type = const.container
# "self" in a constant is the constant's namespace
context.type = const.namespace

# Start with fresh variables
context.vars = LLVMVars.new
3 changes: 2 additions & 1 deletion src/compiler/crystal/command/docs.cr
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ class Crystal::Command
compiler = Compiler.new
compiler.wants_doc = true
result = compiler.top_level_semantic sources
Crystal.generate_docs result.program, included_dirs

Doc::Generator.new(result.program, included_dirs).run
end
end
9 changes: 9 additions & 0 deletions src/compiler/crystal/formatter.cr
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# This file exists so you can do:
#
# ```
# require "compiler/crystal/formatter"
# ```
#
# and use Crystal's formatter programatically, without
# requiring all the semantic and codegen code.

require "./tools/formatter"
38 changes: 19 additions & 19 deletions src/compiler/crystal/macros/macros.cr
Original file line number Diff line number Diff line change
@@ -25,12 +25,12 @@ module Crystal
filename
end

def expand_macro(a_macro : Macro, call : Call, scope : Type, type_lookup : Type?)
macro_expander.expand a_macro, call, scope, type_lookup || scope
def expand_macro(a_macro : Macro, call : Call, scope : Type, path_lookup : Type?)
macro_expander.expand a_macro, call, scope, path_lookup || scope
end

def expand_macro(node : ASTNode, scope : Type, type_lookup : Type?, free_vars = nil)
macro_expander.expand node, scope, type_lookup || scope, free_vars
def expand_macro(node : ASTNode, scope : Type, path_lookup : Type?, free_vars = nil)
macro_expander.expand node, scope, path_lookup || scope, free_vars
end

def parse_macro_source(expanded_macro, the_macro, node, vars, inside_def = false, inside_type = false, inside_exp = false, mode : MacroExpansionMode = MacroExpansionMode::Normal)
@@ -82,15 +82,15 @@ module Crystal
@cache = {} of String => String
end

def expand(a_macro : Macro, call : Call, scope : Type, type_lookup : Type)
visitor = MacroVisitor.new self, @program, scope, type_lookup, a_macro, call
def expand(a_macro : Macro, call : Call, scope : Type, path_lookup : Type)
visitor = MacroVisitor.new self, @program, scope, path_lookup, a_macro, call
a_macro.body.accept visitor
source = visitor.to_s
ExpandedMacro.new source, visitor.yields
end

def expand(node : ASTNode, scope : Type, type_lookup : Type, free_vars = nil)
visitor = MacroVisitor.new self, @program, scope, type_lookup, node.location
def expand(node : ASTNode, scope : Type, path_lookup : Type, free_vars = nil)
visitor = MacroVisitor.new self, @program, scope, path_lookup, node.location
visitor.free_vars = free_vars
node.accept visitor
source = visitor.to_s
@@ -141,7 +141,7 @@ module Crystal
getter yields : Hash(String, ASTNode)?
property free_vars : Hash(String, TypeVar)?

def self.new(expander, mod, scope : Type, type_lookup : Type, a_macro : Macro, call)
def self.new(expander, mod, scope : Type, path_lookup : Type, a_macro : Macro, call)
vars = {} of String => ASTNode
splat_index = a_macro.splat_index
double_splat = a_macro.double_splat
@@ -206,13 +206,13 @@ module Crystal
vars[macro_block_arg.name] = call_block || Nop.new
end

new(expander, mod, scope, type_lookup, a_macro.location, vars, call.block)
new(expander, mod, scope, path_lookup, a_macro.location, vars, call.block)
end

record MacroVarKey, name : String, exps : Array(ASTNode)?

def initialize(@expander : MacroExpander, @program : Program,
@scope : Type, @type_lookup : Type, @location : Location?,
@scope : Type, @path_lookup : Type, @location : Location?,
@vars = {} of String => ASTNode, @block : Block? = nil)
@str = MemoryIO.new(512)
@last = Nop.new
@@ -512,7 +512,7 @@ module Crystal
if node.names.size == 1 && (match = @free_vars.try &.[node.names.first]?)
matched_type = match
else
matched_type = @type_lookup.lookup_type(node)
matched_type = @path_lookup.lookup_path(node)
end

unless matched_type
@@ -528,19 +528,19 @@ module Crystal
# (a tuple type, or a named tuple type) but the user should see
# them as literals, and having them as a type doesn't add
# any useful information.
type_lookup = @type_lookup.instance_type
path_lookup = @path_lookup.instance_type
if node.names.size == 1
case type_lookup
case path_lookup
when UnionType
produce_tuple = node.names.first == "T"
when GenericClassInstanceType
produce_tuple = ((splat_index = type_lookup.splat_index) &&
type_lookup.type_vars.keys.index(node.names.first) == splat_index) ||
(type_lookup.double_variadic? && type_lookup.type_vars.first_key == node.names.first)
produce_tuple = ((splat_index = path_lookup.splat_index) &&
path_lookup.type_vars.keys.index(node.names.first) == splat_index) ||
(path_lookup.double_variadic? && path_lookup.type_vars.first_key == node.names.first)
when IncludedGenericModule
a_module = type_lookup.module
a_module = path_lookup.module
produce_tuple = (splat_index = a_module.splat_index) &&
type_lookup.mapping.keys.index(node.names.first) == splat_index
path_lookup.mapping.keys.index(node.names.first) == splat_index
else
produce_tuple = false
end
99 changes: 44 additions & 55 deletions src/compiler/crystal/semantic.cr
Original file line number Diff line number Diff line change
@@ -14,66 +14,55 @@ require "./semantic/*"
# - cleanup: remove dead code and other simplifications
# - check recursive structs (RecursiveStructChecker): check that structs are not recursive (impossible to codegen)

module Crystal
ThreadLocalAttributes = %w(ThreadLocal)
ValidGlobalAttributes = ThreadLocalAttributes
ValidExternalVarAttributes = ThreadLocalAttributes
ValidClassVarAttributes = ThreadLocalAttributes
ValidStructDefAttributes = %w(Packed)
ValidDefAttributes = %w(AlwaysInline Naked NoInline Raises ReturnsTwice Primitive)
ValidFunDefAttributes = %w(AlwaysInline Naked NoInline Raises ReturnsTwice CallConvention)
ValidEnumDefAttributes = %w(Flags)
class Crystal::Program
# Runs semantic analysis on the given node, returning a node
# that's typed. In the process types and methods are defined in
# this program.
def semantic(node : ASTNode, stats = false) : ASTNode
node, processor = top_level_semantic(node, stats: stats)

class Program
# Runs semantic analysis on the given node, returning a node
# that's typed. In the process types and methods are defined in
# this program.
def semantic(node : ASTNode, stats = false) : ASTNode
node, processor = top_level_semantic(node, stats: stats)

Crystal.timing("Semantic (cvars initializers)", stats) do
visit_class_vars_initializers(node)
end
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
# 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
node.accept InstanceVarsInitializerVisitor.new(self)
end
result = Crystal.timing("Semantic (main)", stats) do
visit_main(node)
end
Crystal.timing("Semantic (cleanup)", stats) do
cleanup_types
cleanup_files
end
Crystal.timing("Semantic (recursive struct check)", stats) do
RecursiveStructChecker.new(self).run
end
result
Crystal.timing("Semantic (ivars initializers)", stats) do
node.accept InstanceVarsInitializerVisitor.new(self)
end
result = Crystal.timing("Semantic (main)", stats) do
visit_main(node)
end
Crystal.timing("Semantic (cleanup)", stats) do
cleanup_types
cleanup_files
end
Crystal.timing("Semantic (recursive struct check)", stats) do
RecursiveStructChecker.new(self).run
end
result
end

# Processes type declarations and instance/class/global vars
# types are guessed or followed according to type annotations.
#
# 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)
end
Crystal.timing("Semantic (new)", stats) do
define_new_methods
end
node, processor = Crystal.timing("Semantic (type declarations)", stats) do
TypeDeclarationProcessor.new(self).process(node)
end
Crystal.timing("Semantic (abstract def check)", stats) do
AbstractDefChecker.new(self).run
end
{node, processor}
# Processes type declarations and instance/class/global vars
# types are guessed or followed according to type annotations.
#
# 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)
end
Crystal.timing("Semantic (new)", stats) do
define_new_methods
end
node, processor = Crystal.timing("Semantic (type declarations)", stats) do
TypeDeclarationProcessor.new(self).process(node)
end
Crystal.timing("Semantic (abstract def check)", stats) do
AbstractDefChecker.new(self).run
end
{node, processor}
end
end
4 changes: 2 additions & 2 deletions src/compiler/crystal/semantic/abstract_def_checker.cr
Original file line number Diff line number Diff line change
@@ -124,8 +124,8 @@ class Crystal::AbstractDefChecker
if r2 && r1 && r1 != r2
# Check if a1.restriction is contravariant with a2.restriction
begin
rt1 = TypeLookup.lookup(t1, r1)
rt2 = TypeLookup.lookup(t2, r2)
rt1 = t1.lookup_type(r1)
rt2 = t2.lookup_type(r2)
return false unless rt2.covariant?(rt1)
rescue Crystal::TypeException
# Ignore if we can't find a type (assume the method is implemented)
Loading