Skip to content

Commit ffa738c

Browse files
committed
disable assert macro unless running in debug mode
1 parent 3559b72 commit ffa738c

File tree

12 files changed

+190
-80
lines changed

12 files changed

+190
-80
lines changed

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ New library functions
8484
efficiently ([#35816]).
8585
* New function `addenv` for adding environment mappings into a `Cmd` object, returning the new `Cmd` object.
8686
* New function `insorted` for determining whether an element is in a sorted collection or not ([#37490]).
87+
* New function `isdebug` for checking if julia is running in debug mode.
88+
Packages use different precompile caches in debug and non-debug mode thus it
89+
safe to use this function for compile time decisions. Also, note that
90+
`@assert` now is a no-op unless `isdebug()` is true. Debug mode is activated
91+
with the `-g2` flag.
8792

8893
New library features
8994
--------------------
@@ -124,6 +129,8 @@ Standard library changes
124129
* The `Pkg.Artifacts` module has been imported as a separate standard library. It is still available as
125130
`Pkg.Artifacts`, however starting from Julia v1.6+, packages may import simply `Artifacts` without importing
126131
all of `Pkg` alongside. ([#37320])
132+
* The `@assert` macro is now only active when julia is started with a debug
133+
level of 2 (`julia -g2`).
127134

128135
#### LinearAlgebra
129136

base/Base.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ time_ns() = ccall(:jl_hrtime, UInt64, ())
6666

6767
start_base_include = time_ns()
6868

69+
julia_debug = true
70+
isdebug() = julia_debug::Bool
71+
6972
## Load essential files and libraries
7073
include("essentials.jl")
7174
include("ctypes.jl")
@@ -396,6 +399,7 @@ in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules
396399

397400
if is_primary_base_module
398401
function __init__()
402+
global julia_debug = Base.JLOptions().debug_level >= 2
399403
# try to ensuremake sure OpenBLAS does not set CPU affinity (#1070, #9639)
400404
if !haskey(ENV, "OPENBLAS_MAIN_FREE") && !haskey(ENV, "GOTOBLAS_MAIN_FREE")
401405
ENV["OPENBLAS_MAIN_FREE"] = "1"

base/error.jl

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,21 +183,25 @@ windowserror(p, b::Bool; extrainfo=nothing) = b ? windowserror(p, extrainfo=extr
183183
windowserror(p, code::UInt32=Libc.GetLastError(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), 0, WindowsErrorInfo(code, extrainfo)))
184184

185185

186-
## assertion macro ##
186+
## assertion and ifdebug macros ##
187+
188+
"""
189+
@ifdebug expr
190+
191+
Equivalent to `@static if isdebug()`
192+
"""
193+
macro ifdebug(expr)
194+
isdefined(@__MODULE__, :isdebug) && !(isdebug()) && return nothing
195+
return :(esc(expr))
196+
end
187197

188198

189199
"""
190200
@assert cond [text]
191201
192202
Throw an [`AssertionError`](@ref) if `cond` is `false`. Preferred syntax for writing assertions.
193-
Message `text` is optionally displayed upon assertion failure.
194-
195-
!!! warning
196-
An assert might be disabled at various optimization levels.
197-
Assert should therefore only be used as a debugging tool
198-
and not used for authentication verification (e.g., verifying passwords),
199-
nor should side effects needed for the function to work correctly
200-
be used inside of asserts.
203+
Message `text` is optionally displayed upon assertion failure. Only active when julia is started
204+
in debug mode (`julia -g`).
201205
202206
# Examples
203207
```jldoctest
@@ -208,6 +212,7 @@ julia> @assert isodd(3) "What even are numbers?"
208212
```
209213
"""
210214
macro assert(ex, msgs...)
215+
isdefined(@__MODULE__, :isdebug) && !(isdebug()) && return nothing
211216
msg = isempty(msgs) ? ex : msgs[1]
212217
if isa(msg, AbstractString)
213218
msg = msg # pass-through

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ export
780780
atreplinit,
781781
exit,
782782
ntuple,
783+
isdebug,
783784

784785
# I/O and events
785786
close,

base/loading.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ function compilecache_path(pkg::PkgId, cache::TOMLCache)::String
12311231
crc = _crc32c(unsafe_string(JLOptions().image_file), crc)
12321232
crc = _crc32c(unsafe_string(JLOptions().julia_bin), crc)
12331233
crc = _crc32c(get_preferences_hash(pkg.uuid, cache), crc)
1234+
crc = _crc32c(isdebug(), crc)
12341235
project_precompile_slug = slug(crc, 5)
12351236
abspath(cachepath, string(entryfile, "_", project_precompile_slug, ".ji"))
12361237
end
@@ -1358,6 +1359,8 @@ function parse_cache_header(f::IO)
13581359
end
13591360
totbytes -= 4 + 4 + n2 + 8
13601361
end
1362+
julia_debug = Bool(read(f, UInt8))
1363+
totbytes -= 1
13611364
prefs_hash = read(f, UInt64)
13621365
totbytes -= 8
13631366
@assert totbytes == 12 "header of cache file appears to be corrupt"
@@ -1372,7 +1375,7 @@ function parse_cache_header(f::IO)
13721375
build_id = read(f, UInt64) # build id
13731376
push!(required_modules, PkgId(uuid, sym) => build_id)
13741377
end
1375-
return modules, (includes, requires), required_modules, srctextpos, prefs_hash
1378+
return modules, (includes, requires), required_modules, srctextpos, prefs_hash, julia_debug
13761379
end
13771380

13781381
function parse_cache_header(cachefile::String; srcfiles_only::Bool=false)
@@ -1381,21 +1384,21 @@ function parse_cache_header(cachefile::String; srcfiles_only::Bool=false)
13811384
!isvalid_cache_header(io) && throw(ArgumentError("Invalid header in cache file $cachefile."))
13821385
ret = parse_cache_header(io)
13831386
srcfiles_only || return ret
1384-
modules, (includes, requires), required_modules, srctextpos, prefs_hash = ret
1387+
modules, (includes, requires), required_modules, srctextpos, prefs_hash, julia_debug = ret
13851388
srcfiles = srctext_files(io, srctextpos)
13861389
delidx = Int[]
13871390
for (i, chi) in enumerate(includes)
13881391
chi.filename srcfiles || push!(delidx, i)
13891392
end
13901393
deleteat!(includes, delidx)
1391-
return modules, (includes, requires), required_modules, srctextpos, prefs_hash
1394+
return modules, (includes, requires), required_modules, srctextpos, prefs_hash, julia_debug
13921395
finally
13931396
close(io)
13941397
end
13951398
end
13961399

13971400
function cache_dependencies(f::IO)
1398-
defs, (includes, requires), modules, srctextpos, prefs_hash = parse_cache_header(f)
1401+
defs, (includes, requires), modules, srctextpos, prefs_hash, julia_debug = parse_cache_header(f)
13991402
return modules, map(chi -> (chi.filename, chi.mtime), includes) # return just filename and mtime
14001403
end
14011404

@@ -1410,7 +1413,7 @@ function cache_dependencies(cachefile::String)
14101413
end
14111414

14121415
function read_dependency_src(io::IO, filename::AbstractString)
1413-
modules, (includes, requires), required_modules, srctextpos, prefs_hash = parse_cache_header(io)
1416+
modules, (includes, requires), required_modules, srctextpos, prefs_hash, julia_debug = parse_cache_header(io)
14141417
srctextpos == 0 && error("no source-text stored in cache file")
14151418
seek(io, srctextpos)
14161419
return _read_dependency_src(io, filename)
@@ -1496,7 +1499,7 @@ function stale_cachefile(modpath::String, cachefile::String, cache::TOMLCache)
14961499
@debug "Rejecting cache file $cachefile due to it containing an invalid cache header"
14971500
return true # invalid cache file
14981501
end
1499-
modules, (includes, requires), required_modules, srctextpos, prefs_hash = parse_cache_header(io)
1502+
modules, (includes, requires), required_modules, srctextpos, prefs_hash, julia_debug = parse_cache_header(io)
15001503
id = isempty(modules) ? nothing : first(modules).first
15011504
modules = Dict{PkgId, UInt64}(modules)
15021505

@@ -1572,6 +1575,11 @@ function stale_cachefile(modpath::String, cachefile::String, cache::TOMLCache)
15721575
end
15731576

15741577
if isa(id, PkgId)
1578+
curr_debug = isdebug()
1579+
if julia_debug != curr_debug
1580+
@debug "Rejecting cache file $cachefile because julia debug mode does not match"
1581+
return true
1582+
end
15751583
curr_prefs_hash = get_preferences_hash(id.uuid, cache)
15761584
if prefs_hash != curr_prefs_hash
15771585
@debug "Rejecting cache file $cachefile because preferences hash does not match 0x$(string(prefs_hash, base=16)) != 0x$(string(curr_prefs_hash, base=16))"

doc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
77
JULIAHOME := $(abspath $(SRCDIR)/..)
88
SRCCACHE := $(abspath $(JULIAHOME)/deps/srccache)
99
include $(JULIAHOME)/Make.inc
10-
JULIA_EXECUTABLE := $(call spawn,$(build_bindir)/julia) --startup-file=no
10+
JULIA_EXECUTABLE := $(call spawn,$(build_bindir)/julia) --startup-file=no -g2
1111

1212
.PHONY: help clean cleanall html pdf deps deploy
1313

src/dump.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,14 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t **udepsp, jl_array_t *
11241124
}
11251125
write_int32(s, 0); // terminator, for ease of reading
11261126

1127+
// Julia debug mode
1128+
jl_value_t *julia_debug = (jl_value_t*)jl_get_global(jl_base_module, jl_symbol("julia_debug"));
1129+
if (julia_debug) {
1130+
write_int8(s, jl_unbox_bool(julia_debug));
1131+
} else {
1132+
write_int8(s, 0);
1133+
}
1134+
11271135
// Calculate Preferences hash for current package.
11281136
jl_value_t *prefs_hash = NULL;
11291137
if (jl_base_module) {

test/assert.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# This file is run from test/misc.jl separately since it needs to run with `-g2`.
2+
using Test
3+
4+
# test @assert macro
5+
@test_throws AssertionError (@assert 1 == 2)
6+
@test_throws AssertionError (@assert false)
7+
@test_throws AssertionError (@assert false "this is a test")
8+
@test_throws AssertionError (@assert false "this is a test" "another test")
9+
@test_throws AssertionError (@assert false :a)
10+
let
11+
try
12+
@assert 1 == 2
13+
error("unexpected")
14+
catch ex
15+
@test isa(ex, AssertionError)
16+
@test occursin("1 == 2", ex.msg)
17+
end
18+
end
19+
# test @assert message
20+
let
21+
try
22+
@assert 1 == 2 "this is a test"
23+
error("unexpected")
24+
catch ex
25+
@test isa(ex, AssertionError)
26+
@test ex.msg == "this is a test"
27+
end
28+
end
29+
# @assert only uses the first message string
30+
let
31+
try
32+
@assert 1 == 2 "this is a test" "this is another test"
33+
error("unexpected")
34+
catch ex
35+
@test isa(ex, AssertionError)
36+
@test ex.msg == "this is a test"
37+
end
38+
end
39+
# @assert calls string() on second argument
40+
let
41+
try
42+
@assert 1 == 2 :random_object
43+
error("unexpected")
44+
catch ex
45+
@test isa(ex, AssertionError)
46+
@test !occursin("1 == 2", ex.msg)
47+
@test occursin("random_object", ex.msg)
48+
end
49+
end
50+
# if the second argument is an expression, c
51+
let deepthought(x, y) = 42
52+
try
53+
@assert 1 == 2 string("the answer to the ultimate question: ",
54+
deepthought(6, 9))
55+
error("unexpected")
56+
catch ex
57+
@test isa(ex, AssertionError)
58+
@test ex.msg == "the answer to the ultimate question: 42"
59+
end
60+
end

test/errorshow.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ let undefvar
332332
@test err_str == "InterruptException:"
333333
err_str = @except_str throw(ArgumentError("not an error")) ArgumentError
334334
@test err_str == "ArgumentError: not an error"
335-
err_str = @except_str @assert(false) AssertionError
335+
err_str = read(`$(Base.julia_cmd()) -g2 -e 'try @assert false; catch e; showerror(stdout, e); end'`, String)
336336
@test err_str == "AssertionError: false"
337337
end
338338

test/exceptions.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ end
369369
end
370370

371371
# issue #36527
372+
s = raw"""
373+
using Test
372374
function f36527()
373375
caught = false
374376
🏡 = Core.eval(Main, :(module asdf36527 end))
@@ -383,6 +385,8 @@ function f36527()
383385
end
384386
385387
@test f36527()
388+
"""
389+
@test success(`$(Base.julia_cmd()) -g2 -e $s`)
386390

387391
# accessing an undefined var in tail position in a catch block
388392
function undef_var_in_catch()

0 commit comments

Comments
 (0)