Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2ca9135
Add OverflowError exception
bcardiff Jun 13, 2018
a89f3d4
Rename args & variables due to new checked keyword
bcardiff Jun 13, 2018
969dcba
Refactor: avoid setting ivar in codegen_binary_op
bcardiff Jun 18, 2018
acd0f90
Refactor: extract codegen_binary_op_add, _sub and _mul methods
bcardiff Jun 18, 2018
e050734
Refactor: move truncate code to each operator
bcardiff Jun 18, 2018
f64630e
Forward AST call operator location to codegen_binary_op
bcardiff Jun 13, 2018
723b260
Keep IntegerType used as a result of codegen_binary_extend_int
bcardiff Jun 13, 2018
ae5984e
Add --overflow-checked and --overflow-unchecked compiler options
bcardiff Jun 13, 2018
4456370
Add checked / unchecked keywords
bcardiff Jun 4, 2018
9ff91be
Add typing rules for OverflowCheckScope
bcardiff Jun 14, 2018
4399d5e
Add migration helper macro __next_unchecked
bcardiff Jun 18, 2018
9c0d02d
Hasher: Mark required unchecked code blocks
bcardiff Jun 18, 2018
5c64f54
Random: Mark required unchecked code blocks
bcardiff Jun 18, 2018
c45b4bb
Code gen: Use llvm overflow intrinsics for checked int addition
bcardiff Jun 18, 2018
30f9e35
Code gen: Add overflow check before truncation
bcardiff Jun 18, 2018
6744d68
Ensure lexical scope of OverflowCheckScope
bcardiff Jun 14, 2018
ed7ab29
Add overflow check policy to bc_flags
bcardiff Jun 15, 2018
e6cb6cb
Code gen: Use llvm overflow intrinsics for checked int subtraction
bcardiff Jun 18, 2018
8c54b65
Code gen: Use llvm overflow intrinsics for checked int multiplication
bcardiff Jun 18, 2018
385bc00
Dwarf: Mark required unchecked code blocks
bcardiff Jun 15, 2018
ef7baa8
FloatPrinter: Mark required unchecked code blocks
bcardiff Jun 18, 2018
380ae97
String: Mark required unchecked code blocks
bcardiff Jun 18, 2018
72f556c
Hash: Mark required unchecked code blocks
bcardiff Jun 18, 2018
7d50f83
Crypto: Mark required unchecked code blocks
bcardiff Jun 18, 2018
683042d
MD5: Mark required unchecked code blocks
bcardiff Jun 18, 2018
1a3a1b2
JSON: Mark required unchecked code blocks
bcardiff Jun 19, 2018
03bf1b0
SHA1: Mark required unchecked code blocks
bcardiff Jun 19, 2018
a0488b9
Time: Mark required unchecked code blocks
bcardiff Jun 19, 2018
0ee4f1f
Compiler: Mark required unchecked code blocks
bcardiff Jun 19, 2018
5ff7481
CI: Make specs run with --overflow-checked
bcardiff Jun 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/ci
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ prepare_system() {

build() {
with_build_env 'make std_spec clean'
with_build_env 'make crystal std_spec compiler_spec docs'
with_build_env 'make crystal'
with_build_env 'make std_spec compiler_spec docs FLAGS=--overflow-checked'
with_build_env 'find samples -name "*.cr" | xargs -L 1 ./bin/crystal build --no-codegen'
with_build_env './bin/crystal tool format --check samples spec src'
}
Expand Down
327 changes: 327 additions & 0 deletions spec/compiler/codegen/overflow_check_scope_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
require "../../spec_helper"

def checked_run(code)
run(code, overflow_check: Crystal::OverflowCheckScope::Policy::Checked)
end

def unchecked_run(code)
run(code, overflow_check: Crystal::OverflowCheckScope::Policy::Unchecked)
end

describe "Code gen: overflow check scope" do
describe "for add" do
it "can be unchecked" do
checked_run(%(
unchecked { 2147483647_i32 + 1_i32 }
)).to_i.should eq(-2147483648_i32)
end

it "can be checked " do
unchecked_run(%(
require "prelude"

x = 0
begin
checked { 2147483647_i32 + 1_i32 }
x = 1
rescue OverflowError
x = 2
end
x
)).to_i.should eq(2)
end

{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64] %}
it "wrap around if unchecked for {{type}}" do
unchecked_run(%(
require "prelude"
{{type}}::MAX + {{type}}.new(1) == {{type}}::MIN
)).to_b.should be_true
end

it "raises if checked for {{type}}" do
checked_run(%(
require "prelude"
begin
{{type}}::MAX + {{type}}.new(1)
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end

it "wrap around if unchecked for {{type}} + Int64" do
unchecked_run(%(
require "prelude"
{{type}}::MAX + 1_i64 == {{type}}::MIN
)).to_b.should be_true
end

it "raises if checked for {{type}} + Int64" do
checked_run(%(
require "prelude"
begin
{{type}}::MAX + 1_i64
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end
{% end %}
end

describe "for sub" do
it "can be unchecked" do
checked_run(%(
unchecked { -2147483648_i32 - 1_i32 }
)).to_i.should eq(2147483647_i32)
end

it "can be checked " do
unchecked_run(%(
require "prelude"

x = 0
begin
checked { -2147483648_i32 - 1_i32 }
x = 1
rescue OverflowError
x = 2
end
x
)).to_i.should eq(2)
end

{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64] %}
it "wrap around if unchecked for {{type}}" do
unchecked_run(%(
require "prelude"
{{type}}::MIN - {{type}}.new(1) == {{type}}::MAX
)).to_b.should be_true
end

it "raises if checked for {{type}}" do
checked_run(%(
require "prelude"
begin
{{type}}::MIN - {{type}}.new(1)
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end

it "wrap around if unchecked for {{type}} - Int64" do
unchecked_run(%(
require "prelude"
{{type}}::MIN - 1_i64 == {{type}}::MAX
)).to_b.should be_true
end

it "raises if checked for {{type}} - Int64" do
checked_run(%(
require "prelude"
begin
{{type}}::MIN - 1_i64
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end
{% end %}
end

describe "for mul" do
it "can be unchecked" do
checked_run(%(
unchecked { 2147483647_i32 * 2_i32 }
)).to_i.should eq(-2_i32)
end

it "can be checked " do
unchecked_run(%(
require "prelude"

x = 0
begin
checked { 2147483647_i32 * 2_i32 }
x = 1
rescue OverflowError
x = 2
end
x
)).to_i.should eq(2)
end

{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64] %}
it "wrap around if unchecked for {{type}}" do
unchecked_run(%(
require "prelude"
({{type}}::MAX / {{type}}.new(2) + {{type}}.new(1)) * {{type}}.new(2) == {{type}}::MIN
)).to_b.should be_true
end

it "raises if checked for {{type}}" do
checked_run(%(
require "prelude"
begin
({{type}}::MAX / {{type}}.new(2) + {{type}}.new(1)) * {{type}}.new(2)
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end

it "wrap around if unchecked for {{type}} + Int64" do
unchecked_run(%(
require "prelude"
({{type}}::MAX / {{type}}.new(2) + {{type}}.new(1)) * 2_i64 == {{type}}::MIN
)).to_b.should be_true
end

it "raises if checked for {{type}} + Int64" do
checked_run(%(
require "prelude"
begin
({{type}}::MAX / {{type}}.new(2) + {{type}}.new(1)) * 2_i64
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end
{% end %}
end

it "obey default checked" do
checked_run(%(
require "prelude"

x = 0
begin
a = 2147483647_i32 + 1_i32
x = 1
rescue OverflowError
x = 2
end
x
)).to_i.should eq(2)
end

it "obey default unchecked" do
unchecked_run(%(
2147483647_i32 + 1_i32
)).to_i.should eq(-2147483648_i32)
end

it "is obeyed in return" do
checked_run(%(
def inc(v)
unchecked {
return v + 1_i8
}
end

inc(127_i8)
)).to_i.should eq(-128)
end

it "can be nested" do
unchecked_run(%(
require "prelude"

begin
checked { unchecked { 2147483647_i32 + 1_i32 } + 2147483647_i32 + 2147483647_i32 + 2_i32 }
0
rescue OverflowError
1
end
)).to_i.should eq(1)
end

describe "work at lexical scope." do
it "is not forwarded to function calls" do
checked_run(%(
require "prelude"

def inc_checked(v)
v + 1_i8
end

def twice(v)
unchecked { inc_checked(inc_checked(v)) }
end

begin
twice(126_i8)
1
rescue OverflowError
2
end
)).to_i.should eq(2)

unchecked_run(%(
require "prelude"

def inc_unchecked(v)
v + 1_i8
end

def twice(v)
checked { inc_unchecked(inc_unchecked(v)) }
end

begin
twice(126_i8)
1
rescue OverflowError
2
end
)).to_i.should eq(1)
end

it "is not forwarded to blocks yields" do
unchecked_run(%(
require "prelude"

def inc_unchecked
yield + 1_i8
end

def foo
checked {
inc_unchecked { 127_i8 }
}
end

foo
)).to_i.should eq(-128_i8)
end

it "is forwarded to blocks" do
unchecked_run(%(
require "prelude"

def inc_unchecked(v)
yield v
end

def foo
checked {
inc_unchecked(127_i8) { |v| v + 1_i8 }
}
1
rescue OverflowError
0
end

foo
)).to_i.should eq(0)
end
end
end
6 changes: 6 additions & 0 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ module Crystal
it_parses "foo[1] /2", Call.new(Call.new("foo".call, "[]", 1.int32), "/", 2.int32)
it_parses "[1] /2", Call.new(([1.int32] of ASTNode).array, "/", 2.int32)

it_parses "checked { 1 + 2 }", OverflowCheckScope.new(OverflowCheckScope::Policy::Checked, Expressions.from([Call.new(1.int32, "+", 2.int32)] of ASTNode))
it_parses "unchecked { 1 + 2 }", OverflowCheckScope.new(OverflowCheckScope::Policy::Unchecked, Expressions.from([Call.new(1.int32, "+", 2.int32)] of ASTNode))

it_parses "!1", Not.new(1.int32)
it_parses "- 1", Call.new(1.int32, "-")
it_parses "+ 1", Call.new(1.int32, "+")
Expand Down Expand Up @@ -181,6 +184,7 @@ module Crystal
extend class struct module enum while until return
next break lib fun alias pointerof sizeof
instance_sizeof typeof private protected asm out
checked unchecked
end
).each do |kw|
assert_syntax_error "def foo(#{kw}); end", "cannot use '#{kw}' as an argument name", 1, 9
Expand Down Expand Up @@ -1643,6 +1647,8 @@ module Crystal
assert_end_location "extend Foo"
assert_end_location "1.as(Int32)"
assert_end_location "puts obj.foo"
assert_end_location "checked { 1 + 1 }"
assert_end_location "unchecked { 1 + 1 }"

assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'"
assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'"
Expand Down
10 changes: 10 additions & 0 deletions spec/compiler/semantic/overflow_check_scope_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require "../../spec_helper"

describe "Semantic: overflow check scope" do
it "type block by expression" do
assert_type("checked { 1 }") { int32 }
assert_type("unchecked { 1 }") { int32 }
assert_type("checked { 1 + 2; '1' }") { char }
assert_type("def foo; unchecked { return 1 }; end; foo") { int32 }
end
end
Loading