require "../../spec_helper"

describe "Code gen: ssa" do
  it "codegens a redefined var" do
    run(<<-CRYSTAL).to_i.should eq(1)
      a = 1.5
      a = 1
      a
      CRYSTAL
  end

  it "codegens a redefined var inside method" do
    run(<<-CRYSTAL).to_i.should eq(1)
      def foo
        a = 1.5
        a = 1
        a
      end

      foo
      CRYSTAL
  end

  it "codegens a redefined var inside method with argument" do
    run(<<-CRYSTAL).to_i.should eq(1)
      def foo(a)
        a = 1
        a
      end

      foo 1.5
      CRYSTAL
  end

  it "codegens declaration of var inside then when false" do
    run(<<-CRYSTAL).to_i.should eq(0)
      struct Nil
        def to_i!
          0
        end
      end

      if 1 == 2
        b = 2
      end
      b.to_i!
      CRYSTAL
  end

  it "codegens declaration of var inside then when true" do
    run(<<-CRYSTAL).to_i.should eq(2)
      struct Nil
        def to_i!
          0
        end
      end

      if 1 == 1
        b = 2
      end
      b.to_i!
      CRYSTAL
  end

  it "codegens a var that is re-assigned in a block" do
    run(<<-CRYSTAL).to_i.should eq(10)
      struct Char
        def to_i!
          10
        end
      end

      def foo
        yield
      end

      a = 1
      foo do
        a = 'a'
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens a var that is re-assigned in a block (1)" do
    run(<<-CRYSTAL).to_i.should eq(10)
      struct Char
        def to_i!
          10
        end
      end

      a = 1
      while a.to_i! == 1
        a = 'a'
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens a var that is re-assigned in a block (2)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Char
        def to_i!
          10
        end
      end

      a = 1
      while 1 == 2
        a = 'a'
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens a var that is declared in a block (1)" do
    run(<<-CRYSTAL).to_i.should eq(0)
      struct Nil
        def to_i!
          0
        end
      end

      while 1 == 2
        a = 1
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens a var that is declared in a block (2)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Nil
        def to_i!
          0
        end
      end

      b = 1
      while b == 1
        a = 1
        b = 2
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens ssa bug with if/else on var" do
    run(<<-CRYSTAL).to_i.should eq(3)
      a = 1 || nil
      if a && false
        b = 2
      elsif a
        b = 3
      else
        b = 4
      end
      b
      CRYSTAL
  end

  it "codegens ssa bug (1)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Nil
        def to_i!
          0
        end
      end

      index = nil
      if index
        a = index
        1
      else
        if 1 == 1
          index = 1
        else
          1
        end
        a = index
        1
      end
      a.to_i!
      CRYSTAL
  end

  it "codegens ssa bug (2)" do
    # This shows a bug where a block variable (coconio in this case)
    # wasn't reset to nil before each block iteration.
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Nil
        def to_i!
          0
        end
      end

      def foo
        i = 1
        while i <= 3
          yield i
          i &+= 1
        end
      end

      a = 0
      foo do |x|
        coconio = x if x == 1
        a &+= coconio.to_i!
      end
      a
      CRYSTAL
  end
end
