Skip to content

Commit

Permalink
Merge branch 'master' into kp/safe-heap-snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth authored Aug 22, 2024
2 parents dbf4bfa + 6d7f4a2 commit c112fcb
Show file tree
Hide file tree
Showing 35 changed files with 238 additions and 188 deletions.
18 changes: 14 additions & 4 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ collect_const_args(arginfo::ArgInfo, start::Int) = collect_const_args(arginfo.ar
function collect_const_args(argtypes::Vector{Any}, start::Int)
return Any[ let a = widenslotwrapper(argtypes[i])
isa(a, Const) ? a.val :
isconstType(a) ? (a::DataType).parameters[1] :
isconstType(a) ? a.parameters[1] :
(a::DataType).instance
end for i = start:length(argtypes) ]
end
Expand Down Expand Up @@ -2063,11 +2063,21 @@ function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name))
fldidx === nothing && return nothing
nminfld = datatype_min_ninitialized(objt)
if ismutabletype(objt)
fldidx == nminfld+1 || return nothing
# A mutable struct can have non-contiguous undefined fields, but `PartialStruct` cannot
# model such a state. So here `PartialStruct` can be used to represent only the
# objects where the field following the minimum initialized fields is also defined.
if fldidx nminfld+1
# if it is already represented as a `PartialStruct`, we can add one more
# `isdefined`-field information on top of those implied by its `fields`
if !(obj isa PartialStruct && fldidx == length(obj.fields)+1)
return nothing
end
end
else
fldidx > nminfld || return nothing
end
return PartialStruct(objt0, Any[fieldtype(objt0, i) for i = 1:fldidx])
return PartialStruct(objt0, Any[obj isa PartialStruct && ilength(obj.fields) ?
obj.fields[i] : fieldtype(objt0,i) for i = 1:fldidx])
end

function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta)
Expand Down Expand Up @@ -2829,7 +2839,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :globaldecl
return RTEffects(Nothing, Any, EFFECTS_UNKNOWN)
elseif ehead === :thunk
return RTEffects(Any, Any, EFFECTS_UNKNOWN)
return RTEffects(Any, Any, Effects())
end
# N.B.: abstract_eval_value_expr can modify the global effects, but
# we move out any arguments with effects during SSA construction later
Expand Down
10 changes: 5 additions & 5 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,12 @@ const NOUB_IF_NOINBOUNDS = 0x01 << 1
# :nonoverlayed bits
const CONSISTENT_OVERLAY = 0x01 << 1

const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)

function Effects(effects::Effects = _EFFECTS_UNKNOWN;
function Effects(effects::Effects=Effects(
ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false);
consistent::UInt8 = effects.consistent,
effect_free::UInt8 = effects.effect_free,
nothrow::Bool = effects.nothrow,
Expand Down
10 changes: 8 additions & 2 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM

has_flag(curr::UInt32, flag::UInt32) = (curr & flag) == flag

function iscallstmt(@nospecialize stmt)
stmt isa Expr || return false
head = stmt.head
return head === :call || head === :invoke || head === :foreigncall
end

function flags_for_effects(effects::Effects)
flags = zero(UInt32)
if is_consistent(effects)
Expand Down Expand Up @@ -380,7 +386,7 @@ function recompute_effects_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt),
elseif nothrow
flag |= IR_FLAG_NOTHROW
end
if !(isexpr(stmt, :call) || isexpr(stmt, :invoke))
if !iscallstmt(stmt)
# There is a bit of a subtle point here, which is that some non-call
# statements (e.g. PiNode) can be UB:, however, we consider it
# illegal to introduce such statements that actually cause UB (for any
Expand Down Expand Up @@ -784,7 +790,7 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState)
if !has_flag(flag, IR_FLAG_NORTCALL)
# if a function call that might invoke `Core.Compiler.return_type` has been deleted,
# there's no need to taint with `:nortcall`, allowing concrete evaluation
if isexpr(stmt, :call) || isexpr(stmt, :invoke)
if iscallstmt(stmt)
sv.nortcall = false
end
end
Expand Down
3 changes: 2 additions & 1 deletion base/compiler/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Core.PhiNode() = Core.PhiNode(Int32[], Any[])

isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave)
isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) ||
isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave)

struct CFG
blocks::Vector{BasicBlock}
Expand Down
3 changes: 2 additions & 1 deletion base/compiler/ssair/irinterp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction,
rt = nothing
if isa(stmt, Expr)
head = stmt.head
if head === :call || head === :foreigncall || head === :new || head === :splatnew || head === :static_parameter || head === :isdefined || head === :boundscheck
if (head === :call || head === :foreigncall || head === :new || head === :splatnew ||
head === :static_parameter || head === :isdefined || head === :boundscheck)
(; rt, effects) = abstract_eval_statement_expr(interp, stmt, nothing, irsv)
add_flag!(inst, flags_for_effects(effects))
elseif head === :invoke
Expand Down
7 changes: 6 additions & 1 deletion base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2966,7 +2966,7 @@ end
function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any},
sv::AbsIntState, max_methods::Int)
length(argtypes) < 2 && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())
isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_UNKNOWN, NoCallInfo())
isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo())
argtypes = argtypes[2:end]
atype = argtypes_to_type(argtypes)
matches = find_method_matches(interp, argtypes, atype; max_methods)
Expand Down Expand Up @@ -3191,6 +3191,11 @@ function foreigncall_effects(@specialize(abstract_eval), e::Expr)
elseif name === :jl_genericmemory_copy_slice
return Effects(EFFECTS_TOTAL; consistent=CONSISTENT_IF_NOTRETURNED, nothrow=false)
end
# `:foreigncall` can potentially perform all sorts of operations, including calling
# overlay methods, but the `:foreigncall` itself is not dispatched, and there is no
# concern that the method calls that potentially occur within the `:foreigncall` will
# be executed using the wrong method table due to concrete evaluation, so using
# `EFFECTS_UNKNOWN` here and not tainting with `:nonoverlayed` is fine
return EFFECTS_UNKNOWN
end

Expand Down
2 changes: 1 addition & 1 deletion base/compiler/typeutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function hasuniquerep(@nospecialize t)
iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system
isconcretetype(t) && return true # these are also interned and pointer comparable
if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes
return _all(hasuniquerep, t.parameters)
return all(hasuniquerep, t.parameters)
end
return false
end
Expand Down
4 changes: 3 additions & 1 deletion base/compiler/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ end

function is_valid_rvalue(@nospecialize(x))
is_valid_argument(x) && return true
if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, :invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast, :new_opaque_closure)
if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call,
:invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast,
:new_opaque_closure)
return true
end
return false
Expand Down
6 changes: 4 additions & 2 deletions base/strings/string.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}})
ccall(:jl_cstr_to_string, Ref{String}, (Ptr{UInt8},), p)
end

# This is @assume_effects :effect_free :nothrow :terminates_globally @ccall jl_alloc_string(n::Csize_t)::Ref{String},
# This is `@assume_effects :total !:consistent @ccall jl_alloc_string(n::Csize_t)::Ref{String}`,
# but the macro is not available at this time in bootstrap, so we write it manually.
@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall,0x000e)), :(convert(Csize_t, n))))
const _string_n_override = 0x04ee
@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String},
:(Core.svec(Csize_t)), 1, QuoteNode((:ccall, _string_n_override)), :(convert(Csize_t, n))))

"""
String(s::AbstractString)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/OpenBLAS_jll/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ else
end

@testset "OpenBLAS_jll" begin
@test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) != nothing
@test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) !== nothing
end
18 changes: 9 additions & 9 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -591,32 +591,32 @@ end
@test findall(!, m) == [k for (k,v) in pairs(m) if !v]
@test findfirst(!iszero, a) == 2
@test findfirst(a.==0) == 1
@test findfirst(a.==5) == nothing
@test findfirst(a.==5) === nothing
@test findfirst(Dict(1=>false, 2=>true)) == 2
@test findfirst(Dict(1=>false)) == nothing
@test findfirst(Dict(1=>false)) === nothing
@test findfirst(isequal(3), [1,2,4,1,2,3,4]) == 6
@test findfirst(!isequal(1), [1,2,4,1,2,3,4]) == 2
@test findfirst(isodd, [2,4,6,3,9,2,0]) == 4
@test findfirst(isodd, [2,4,6,2,0]) == nothing
@test findfirst(isodd, [2,4,6,2,0]) === nothing
@test findnext(!iszero,a,4) == 4
@test findnext(!iszero,a,5) == 6
@test findnext(!iszero,a,1) == 2
@test findnext(isequal(1),a,4) == 6
@test findnext(isequal(5),a,4) == nothing
@test findnext(isequal(5),a,4) === nothing
@test findlast(!iszero, a) == 8
@test findlast(a.==0) == 5
@test findlast(a.==5) == nothing
@test findlast(false) == nothing # test non-AbstractArray findlast
@test findlast(a.==5) === nothing
@test findlast(false) === nothing # test non-AbstractArray findlast
@test findlast(isequal(3), [1,2,4,1,2,3,4]) == 6
@test findlast(isodd, [2,4,6,3,9,2,0]) == 5
@test findlast(isodd, [2,4,6,2,0]) == nothing
@test findlast(isodd, [2,4,6,2,0]) === nothing
@test findprev(!iszero,a,4) == 4
@test findprev(!iszero,a,5) == 4
@test findprev(!iszero,a,1) == nothing
@test findprev(!iszero,a,1) === nothing
@test findprev(isequal(1),a,4) == 2
@test findprev(isequal(1),a,8) == 6
@test findprev(isodd, [2,4,5,3,9,2,0], 7) == 5
@test findprev(isodd, [2,4,5,3,9,2,0], 2) == nothing
@test findprev(isodd, [2,4,5,3,9,2,0], 2) === nothing
@test findfirst(isequal(0x00), [0x01, 0x00]) == 2
@test findlast(isequal(0x00), [0x01, 0x00]) == 2
@test findnext(isequal(0x00), [0x00, 0x01, 0x00], 2) == 3
Expand Down
34 changes: 17 additions & 17 deletions test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1357,11 +1357,11 @@ timesofar("find")
@test findprev(b1, 777) == findprevnot(b2, 777) == findprev(!, b2, 777) == 777
@test findprev(b1, 776) == findprevnot(b2, 776) == findprev(!, b2, 776) == 77
@test findprev(b1, 77) == findprevnot(b2, 77) == findprev(!, b2, 77) == 77
@test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) == nothing
@test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) == nothing
@test findprev(identity, b1, -1) == nothing
@test findprev(Returns(false), b1, -1) == nothing
@test findprev(Returns(true), b1, -1) == nothing
@test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) === nothing
@test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) === nothing
@test findprev(identity, b1, -1) === nothing
@test findprev(Returns(false), b1, -1) === nothing
@test findprev(Returns(true), b1, -1) === nothing
@test_throws BoundsError findnext(b1, -1)
@test_throws BoundsError findnextnot(b2, -1)
@test_throws BoundsError findnext(!, b2, -1)
Expand All @@ -1372,28 +1372,28 @@ timesofar("find")
@test findnext(b1, 77) == findnextnot(b2, 77) == findnext(!, b2, 77) == 77
@test findnext(b1, 78) == findnextnot(b2, 78) == findnext(!, b2, 78) == 777
@test findnext(b1, 777) == findnextnot(b2, 777) == findnext(!, b2, 777) == 777
@test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) == nothing
@test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) == nothing
@test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) == nothing
@test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) === nothing
@test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) === nothing
@test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) === nothing

@test findlast(b1) == Base.findlastnot(b2) == 777
@test findfirst(b1) == Base.findfirstnot(b2) == 77

b0 = BitVector()
@test findprev(Returns(true), b0, -1) == nothing
@test findprev(Returns(true), b0, -1) === nothing
@test_throws BoundsError findprev(Returns(true), b0, 1)
@test_throws BoundsError findnext(Returns(true), b0, -1)
@test findnext(Returns(true), b0, 1) == nothing
@test findnext(Returns(true), b0, 1) === nothing

b1 = falses(10)
@test findprev(Returns(true), b1, 5) == 5
@test findnext(Returns(true), b1, 5) == 5
@test findprev(Returns(true), b1, -1) == nothing
@test findnext(Returns(true), b1, 11) == nothing
@test findprev(Returns(false), b1, 5) == nothing
@test findnext(Returns(false), b1, 5) == nothing
@test findprev(Returns(false), b1, -1) == nothing
@test findnext(Returns(false), b1, 11) == nothing
@test findprev(Returns(true), b1, -1) === nothing
@test findnext(Returns(true), b1, 11) === nothing
@test findprev(Returns(false), b1, 5) === nothing
@test findnext(Returns(false), b1, 5) === nothing
@test findprev(Returns(false), b1, -1) === nothing
@test findnext(Returns(false), b1, 11) === nothing
@test_throws BoundsError findprev(Returns(true), b1, 11)
@test_throws BoundsError findnext(Returns(true), b1, -1)

Expand All @@ -1415,7 +1415,7 @@ timesofar("find")
for l = [1, 63, 64, 65, 127, 128, 129]
f = falses(l)
t = trues(l)
@test findprev(f, l) == findprevnot(t, l) == nothing
@test findprev(f, l) == findprevnot(t, l) === nothing
@test findprev(t, l) == findprevnot(f, l) == l
b1 = falses(l)
b1[end] = true
Expand Down
2 changes: 1 addition & 1 deletion test/channels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ end
c = Channel(1)
close(c)
@test !isopen(c)
c.excp == nothing # to trigger the branch
c.excp === nothing # to trigger the branch
@test_throws InvalidStateException Base.check_channel_state(c)
end

Expand Down
2 changes: 1 addition & 1 deletion test/char.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ end
#iterate(c::Char)
for x in testarrays
@test iterate(x)[1] == x
@test iterate(x, iterate(x)[2]) == nothing
@test iterate(x, iterate(x)[2]) === nothing
end

#isless(x::Char, y::Integer) = isless(UInt32(x), y)
Expand Down
2 changes: 1 addition & 1 deletion test/compiler/codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ function f1_30093(r)
end
end

@test f1_30093(Ref(0)) == nothing
@test f1_30093(Ref(0)) === nothing

# issue 33590
function f33590(b, x)
Expand Down
5 changes: 5 additions & 0 deletions test/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1361,3 +1361,8 @@ end |> Core.Compiler.is_nothrow
@test Base.infer_effects((Vector{Any},)) do xs
Core.svec(xs...)
end |> Core.Compiler.is_nothrow

# effects for unknown `:foreigncall`s
@test Base.infer_effects() do
@ccall unsafecall()::Cvoid
end == Core.Compiler.EFFECTS_UNKNOWN
20 changes: 18 additions & 2 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,7 @@ gl_17003 = [1, 2, 3]
f2_17003(item::AVector_17003) = nothing
f2_17003(::Any) = f2_17003(NArray_17003(gl_17003))

@test f2_17003(1) == nothing
@test f2_17003(1) === nothing

# issue #20847
function segfaultfunction_20847(A::Vector{NTuple{N, T}}) where {N, T}
Expand All @@ -1076,7 +1076,7 @@ end
tuplevec_20847 = Tuple{Float64, Float64}[(0.0,0.0), (1.0,0.0)]

for A in (1,)
@test segfaultfunction_20847(tuplevec_20847) == nothing
@test segfaultfunction_20847(tuplevec_20847) === nothing
end

# Issue #20902, check that this doesn't error.
Expand Down Expand Up @@ -6033,6 +6033,22 @@ end |> Core.Compiler.is_nothrow
return x.b
end
end |> !Core.Compiler.is_nothrow
@test Base.infer_effects((PartiallyInitialized2,); optimize=false) do x
if isdefined(x, :b)
if isdefined(x, :c)
return x.c
end
return x.b
end
return nothing
end |> Core.Compiler.is_nothrow
@test Base.infer_effects((Bool,Int,); optimize=false) do c, b
x = c ? PartiallyInitialized1(true) : PartiallyInitialized1(true, b)
if isdefined(x, :b)
return Val(x.a), x.b
end
return nothing
end |> Core.Compiler.is_nothrow

# End to end test case for the partially initialized struct with `PartialStruct`
@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing)
Expand Down
2 changes: 1 addition & 1 deletion test/compiler/ssair.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ let code = Any[
]
ir = make_ircode(code; verify=false)
ir = Core.Compiler.compact!(ir, true)
@test Core.Compiler.verify_ir(ir) == nothing
@test Core.Compiler.verify_ir(ir) === nothing
end

# issue #37919
Expand Down
2 changes: 1 addition & 1 deletion test/copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ end
bar = Bar19921(foo, Dict(foo => 3))
bar2 = deepcopy(bar)
@test bar2.foo keys(bar2.fooDict)
@test bar2.fooDict[bar2.foo] != nothing
@test bar2.fooDict[bar2.foo] !== nothing
end

let d = IdDict(rand(2) => rand(2) for i = 1:100)
Expand Down
2 changes: 1 addition & 1 deletion test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7031,7 +7031,7 @@ translate27368(::Type{Val{name}}) where {name} =
# issue #27456
@inline foo27456() = try baz_nonexistent27456(); catch; nothing; end
bar27456() = foo27456()
@test bar27456() == nothing
@test bar27456() === nothing

# issue #27365
mutable struct foo27365
Expand Down
6 changes: 3 additions & 3 deletions test/corelogging.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ end
end
@test length(logger.logs) == 1
record = logger.logs[1]
@test record._module == nothing
@test record.file == nothing
@test record.line == nothing
@test record._module === nothing
@test record.file === nothing
@test record.line === nothing
end

# PR #28209
Expand Down
Loading

0 comments on commit c112fcb

Please sign in to comment.