From db5d46d33d80a6a7245245f21e1da9bc798d33e4 Mon Sep 17 00:00:00 2001 From: Sam O'Connor Date: Sat, 12 Mar 2016 16:25:22 +1100 Subject: [PATCH] add @require, like @assert but thows ArgumentError, e.g. function split{T}(c::T, n::Int) @require n > 0 return SplitIterator{T}(c, n) end split("xxx", 0) ERROR: ArgumentError: split(::ASCIIString, ::Int64) requires n > 0 --- base/contracts.jl | 21 +++++++++++++++++++++ base/exports.jl | 1 + base/sysimg.jl | 2 ++ doc/stdlib/base.rst | 6 ++++++ test/misc.jl | 25 +++++++++++++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 base/contracts.jl diff --git a/base/contracts.jl b/base/contracts.jl new file mode 100644 index 0000000000000..b9ffc28dcc124 --- /dev/null +++ b/base/contracts.jl @@ -0,0 +1,21 @@ +# This file is a part of Julia. License is MIT: http://julialang.org/license + + +@noinline function precondition_error(msg) + io = IOBuffer() + StackTraces.show_spec_linfo(io, StackTraces.lookup(backtrace()[3])) + throw(ArgumentError("$(takebuf_string(io)) requires $msg")) +end + + +""" + @require precondition [message] + +Throw `ArgumentError` if `precondition` is false. +""" +macro require(precondition, msg = string(precondition)) + esc(:(if ! $precondition Base.precondition_error($msg) end)) +end +# FIXME +# Should this have a branch-prediction hint? (same for @assert?) +# http://llvm.org/docs/BranchWeightMetadata.html#built-in-expect-instructions diff --git a/base/exports.jl b/base/exports.jl index 4f0601ca50fae..83056577962fa 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1436,6 +1436,7 @@ export @noinline, @assert, + @require, @enum, @label, @goto, diff --git a/base/sysimg.jl b/base/sysimg.jl index 8ad07fae6b782..851105ad4b854 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -90,6 +90,8 @@ include("arraymath.jl") include("simdloop.jl") importall .SimdLoop +include("contracts.jl") + # map-reduce operators include("reduce.jl") diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 684d6e62e99af..b92123a552637 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -1068,6 +1068,12 @@ Errors Throw an ``AssertionError`` if ``cond`` is ``false``\ . Preferred syntax for writing assertions. Message ``text`` is optionally displayed upon assertion failure. +.. function:: @require precondition [message] + + .. Docstring generated from Julia source + + Throw ``ArgumentError`` if ``precondition`` is false. + .. function:: ArgumentError(msg) .. Docstring generated from Julia source diff --git a/test/misc.jl b/test/misc.jl index 1bd0217b43fa5..27a9c8b67773e 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -101,6 +101,31 @@ let deepthought(x, y) = 42 end end +# test @require +let + f1(n) = begin @require n > 0; n end + f2(n) = begin @require n != 0 "non-zero n"; n end + + @test f1(1) == 1 + @test f2(1) == 1 + + try + f1(0) + error("unexpected") + catch ex + @test isa(ex, ArgumentError) + @test ismatch(r"f1(.*) requires n > 0", ex.msg) + end + + try + f2(0) + error("unexpected") + catch ex + @test isa(ex, ArgumentError) + @test ismatch(r"f2(.*) requires non-zero n", ex.msg) + end +end + let # test the process title functions, issue #9957 oldtitle = Sys.get_process_title() Sys.set_process_title("julia0x1")