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: 204c94c39702
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: 917971dedc08
Choose a head ref
  • 3 commits
  • 8 files changed
  • 2 contributors

Commits on Mar 23, 2017

  1. Fix self restriction with including generic module

    Ref: #3847
    
    Now, we can get a compile error with such a code:
    
        module Foo(T)
          def foo(x : T)
            x
          end
        end
    
        abstract struct Bar
          include Foo(self)
        end
    
        struct Baz1 < Bar
        end
    
        struct Baz2 < Bar
        end
    
        Baz1.new.foo Baz2.new # => no overload matches 'Baz1#foo' with type Baz2
    
    This commit adds `lazy_self` parameter to `lookup_type`. When `lazy_self`
    is `true`, `lookup_type` keeps `self` in generics type. It is used to
    look up type for `include` and `extend`.
    makenowjust authored and Brian J. Cardiff committed Mar 23, 2017
    Copy the full SHA
    6645b8f View commit details
  2. Add TODO for fixing self restriction

    Because old compiler wants this definition and CI uses old compiler...
    makenowjust authored and Brian J. Cardiff committed Mar 23, 2017
    Copy the full SHA
    8ace6d4 View commit details
  3. Merge pull request #3972 from MakeNowJust/fix/crystal/self-restriction

    Fix self restriction with including generic module
    bcardiff authored Mar 23, 2017
    Copy the full SHA
    917971d View commit details
16 changes: 16 additions & 0 deletions spec/compiler/semantic/macro_spec.cr
Original file line number Diff line number Diff line change
@@ -775,6 +775,22 @@ describe "Semantic: macro" do
)) { int32.metaclass }
end

it "finds generic type argument of included module with self" do
assert_type(%(
module Bar(T)
def t
{{ T }}
end
end
class Foo(U)
include Bar(self)
end
Foo(Int32).new.t
)) { generic_class("Foo", int32).metaclass }
end

it "finds free type vars" do
assert_type(%(
module Foo(T)
172 changes: 170 additions & 2 deletions spec/compiler/semantic/module_spec.cr
Original file line number Diff line number Diff line change
@@ -226,15 +226,183 @@ describe "Semantic: module" do
end
end
class Baz(X)
class Bar(U)
include Foo(self)
end
Bar(Int32).new.foo
") { generic_class("Bar", int32).metaclass }
end

it "includes generic module with self, and inherits it" do
assert_type("
module Foo(T)
def foo
T
end
end
class Bar(U)
include Foo(self)
end
class Baz < Bar(Int32)
end
Baz.new.foo
") { types["Baz"].metaclass }
end

it "includes generic module with self (check argument type, success)" do
assert_type("
module Foo(T)
def foo(x : T)
x
end
end
class Bar(U)
include Foo(self)
end
Bar(Int32).new.foo Bar(Int32).new
") { generic_class("Bar", int32) }
end

it "includes generic module with self (check argument superclass type, success)" do
assert_type("
module Foo(T)
def foo(x : T)
x
end
end
class Bar(U)
include Foo(self)
end
class Baz < Bar(Int32)
end
Bar(Int32).new.foo Baz.new
") { types["Baz"] }
end

it "includes generic module with self (check argument type, error)" do
assert_error "
module Foo(T)
def foo(x : T)
x
end
end
class Bar(U)
include Foo(self)
end
class Baz1 < Bar(Int32)
end
class Baz2 < Bar(Int32)
end
Baz1.new.foo Baz2.new
", "no overload matches"
end

it "includes generic module with self (check argument superclass type, error)" do
assert_error "
module Foo(T)
def foo(x : T)
x
end
end
class Bar(U)
include Foo(self)
end
class Baz < Bar(Int32)
end
Baz.new.foo Bar(Int32).new
", "no overload matches"
end

it "includes generic module with self (check return type, success)" do
assert_type("
module Foo(T)
def foo : T
Bar(Int32).new
end
end
class Bar(U)
include Foo(self)
end
Bar(Int32).new.foo
") { generic_class("Bar", int32).metaclass }
") { generic_class("Bar", int32) }
end

it "includes generic module with self (check return subclass type, success)" do
assert_type("
module Foo(T)
def foo : T
Baz.new
end
end
class Bar(U)
include Foo(self)
end
class Baz < Bar(Int32)
end
Bar(Int32).new.foo
") { types["Baz"] }
end

it "includes generic module with self (check return type, error)" do
assert_error "
module Foo(T)
def foo : T
Bar(Int32).new
end
end
class Bar(U)
include Foo(self)
end
class Baz < Bar(Int32)
end
Baz.new.foo
", "type must be Baz, not Bar(Int32)"
end

it "includes generic module with self (check return subclass type, error)" do
assert_error "
module Foo(T)
def foo : T
Baz2.new
end
end
class Bar(U)
include Foo(self)
end
class Baz1 < Bar(Int32)
end
class Baz2 < Bar(Int32)
end
Baz1.new.foo
", "type must be Baz1, not Baz2"
end

it "includes module but can't access metaclass methods" do
3 changes: 3 additions & 0 deletions src/compiler/crystal/macros/interpreter.cr
Original file line number Diff line number Diff line change
@@ -418,6 +418,9 @@ module Crystal
end

TypeNode.new(matched_type)
when Self
target = @scope == @program.class_type ? @scope : @scope.instance_type
TypeNode.new(target)
when ASTNode
matched_type
else
17 changes: 11 additions & 6 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
@@ -177,6 +177,8 @@ module Crystal
# It's different if from a virtual type we do `v.class.new`
# because the class could be any in the hierarchy.
node.type = check_type_in_type_args(type.remove_alias_if_simple).devirtualize
when Self
node.type = check_type_in_type_args(the_self(node).remove_alias_if_simple)
when ASTNode
type.accept self unless type.type?
node.syntax_replacement = type
@@ -310,12 +312,7 @@ module Crystal
end

def visit(node : Self)
the_self = (@scope || current_type)
if the_self.is_a?(Program)
node.raise "there's no self in this scope"
end

node.type = the_self.instance_type
node.type = the_self(node).instance_type
end

def visit(node : Var)
@@ -3124,6 +3121,14 @@ module Crystal
end
end

def the_self(node)
the_self = (@scope || current_type)
if the_self.is_a?(Program)
node.raise "there's no self in this scope"
end
the_self
end

def visit(node : When | Unless | Until | MacroLiteral | OpAssign)
raise "BUG: #{node.class_desc} node '#{node}' (#{node.location}) should have been eliminated in normalize"
end
4 changes: 2 additions & 2 deletions src/compiler/crystal/semantic/semantic_visitor.cr
Original file line number Diff line number Diff line change
@@ -220,8 +220,8 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor
end
end

def lookup_type(node : ASTNode, free_vars = nil)
current_type.lookup_type(node, free_vars: free_vars, allow_typeof: false)
def lookup_type(node : ASTNode, free_vars = nil, lazy_self = false)
current_type.lookup_type(node, free_vars: free_vars, allow_typeof: false, lazy_self: lazy_self)
end

def check_outside_exp(node, op)
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
@@ -811,7 +811,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
def include_in(current_type, node, kind)
node_name = node.name

type = lookup_type(node_name)
type = lookup_type(node_name, lazy_self: true)
case type
when GenericModuleType
node.raise "wrong number of type vars for #{type} (given 0, expected #{type.type_vars.size})"
Loading