|
| 1 | +using Test |
| 2 | + |
| 3 | +function invalidate_struct(m::Module, name::Symbol) |
| 4 | + # Delete the constructors for the old type, in an attempt to ensure that |
| 5 | + # old objects can't be created anymore. |
| 6 | + T = getfield(m, name) |
| 7 | + for constructor in methods(T) |
| 8 | + Base.delete_method(constructor) |
| 9 | + end |
| 10 | + # Rename the old type (move it "out of the way") |
| 11 | + new_name = gensym(name) |
| 12 | + return Base.rename_binding(m, name, new_name) |
| 13 | +end |
| 14 | + |
| 15 | +## In the first set of tests, we redefine the types before using them |
| 16 | +include("testhelpers/RedefStruct.jl") |
| 17 | + |
| 18 | +oldtype = invalidate_struct(RedefStruct, :A) |
| 19 | +ex = quote |
| 20 | + struct A |
| 21 | + x::Int8 # change the field type |
| 22 | + end |
| 23 | +end |
| 24 | +Core.eval(RedefStruct, ex) |
| 25 | +@test fieldtype(RedefStruct.A, :x) === Int8 |
| 26 | +a = RedefStruct.A(-1) |
| 27 | +@test a.x === Int8(-1) |
| 28 | +@test_throws MethodError RedefStruct.fA(a) # this method was defined for the old A |
| 29 | +aa = RedefStruct.gA(5) |
| 30 | +@test !isa(aa, oldtype) |
| 31 | +@test aa.x === Int8(5) |
| 32 | + |
| 33 | +oldtype = invalidate_struct(RedefStruct, :B) |
| 34 | +ex = quote |
| 35 | + struct B{T<:Integer} |
| 36 | + z::T |
| 37 | + end |
| 38 | +end |
| 39 | +Core.eval(RedefStruct, ex) |
| 40 | +@test fieldtype(RedefStruct.B{Int16}, :z) === Int16 |
| 41 | +b = RedefStruct.B{Int16}(-1) |
| 42 | +@test b.z === Int16(-1) |
| 43 | +@test_throws MethodError RedefStruct.fB(b) |
| 44 | +# It's not possible to directly construct the TypeError that gets returned, so let's cheat |
| 45 | +typerr = try RedefStruct.gB(Int) catch err err end |
| 46 | +@test isa(typerr, TypeError) && typerr.func == :B && typerr.context == "T" && |
| 47 | + typerr.expected.name == :T && typerr.expected.ub === Integer && typerr.got === Float64 |
| 48 | +Core.eval(RedefStruct, :(gB(::Type{T}) where T = B{unsigned(T)}(2))) |
| 49 | +bb = RedefStruct.gB(Int) |
| 50 | +@test !isa(bb, oldtype) |
| 51 | +@test bb.z === UInt(2) |
| 52 | + |
| 53 | +oldtype = invalidate_struct(RedefStruct, :C) |
| 54 | +ex = quote |
| 55 | + struct C{T,N,A<:AbstractArray{T,N}} |
| 56 | + data::A |
| 57 | + |
| 58 | + function C{T,N,A}(data::AbstractArray) where {T,N,A} |
| 59 | + return new{T,N,A}(data) |
| 60 | + end |
| 61 | + end |
| 62 | +end |
| 63 | +Core.eval(RedefStruct, ex) |
| 64 | +c32 = RedefStruct.C{Float32,2,Array{Float32,2}}([0.1 0.2; 0.3 0.4]) |
| 65 | +@test c32.data isa Array{Float32,2} |
| 66 | +@test_throws MethodError RedefStruct.C([0.1 0.2; 0.3 0.4]) # outer constructor is not yet defined |
| 67 | +Core.eval(RedefStruct, :(C(data::AbstractArray{T,N}) where {T,N} = C{T,N,typeof(data)}(data))) |
| 68 | +c64 = RedefStruct.C([0.1 0.2; 0.3 0.4]) |
| 69 | +@test c64.data isa Array{Float64,2} |
| 70 | +@test_throws MethodError RedefStruct.fC(c32) |
| 71 | +cc = RedefStruct.gC((3,2)) |
| 72 | +@test cc.data == zeros(3, 2) && isa(cc.data, Matrix{Float64}) |
| 73 | + |
| 74 | + |
| 75 | +## Do it again, this time having already used the old methods |
| 76 | +include("testhelpers/RedefStruct.jl") |
| 77 | + |
| 78 | +a = RedefStruct.gA(3) |
| 79 | +@test RedefStruct.fA(a) |
| 80 | +oldtype = invalidate_struct(RedefStruct, :A) |
| 81 | +ex = quote |
| 82 | + struct A |
| 83 | + x::Int8 # change the field type |
| 84 | + end |
| 85 | +end |
| 86 | +Core.eval(RedefStruct, ex) |
| 87 | +@test fieldtype(RedefStruct.A, :x) === Int8 |
| 88 | +a = RedefStruct.A(-1) |
| 89 | +@test a.x === Int8(-1) |
| 90 | +@test_throws MethodError RedefStruct.fA(a) # this method was defined for the old A |
| 91 | +aa = RedefStruct.gA(5) |
| 92 | +@test !isa(aa, oldtype) |
| 93 | +@test aa.x === Int8(5) |
| 94 | + |
| 95 | +b = RedefStruct.gB(Int) |
| 96 | +@test b isa RedefStruct.B{Float64} |
| 97 | +@test RedefStruct.fB(b) |
| 98 | +oldtype = invalidate_struct(RedefStruct, :B) |
| 99 | +ex = quote |
| 100 | + struct B{T<:Integer} |
| 101 | + z::T |
| 102 | + end |
| 103 | +end |
| 104 | +Core.eval(RedefStruct, ex) |
| 105 | +@test fieldtype(RedefStruct.B{Int16}, :z) === Int16 |
| 106 | +b = RedefStruct.B{Int16}(-1) |
| 107 | +@test b.z === Int16(-1) |
| 108 | +@test_throws MethodError RedefStruct.fB(b) |
| 109 | +typerr = try RedefStruct.gB(Int) catch err err end |
| 110 | +@test_broken isa(typerr, TypeError) && typerr.func == :B && typerr.context == "T" && |
| 111 | + typerr.expected.name == :T && typerr.expected.ub === Integer && typerr.got === Float64 |
| 112 | +typerr = try RedefStruct.gB(Float32) catch err err end |
| 113 | +@test isa(typerr, TypeError) && typerr.func == :B && typerr.context == "T" && |
| 114 | + typerr.expected.name == :T && typerr.expected.ub === Integer && typerr.got === Float32 |
| 115 | +Core.eval(RedefStruct, :(gB(::Type{T}) where T = B{unsigned(T)}(2))) |
| 116 | +bb = RedefStruct.gB(Int) |
| 117 | +@test !isa(bb, oldtype) |
| 118 | +@test bb.z === UInt(2) |
| 119 | + |
| 120 | +c = RedefStruct.gC((3,2)) |
| 121 | +@test RedefStruct.fC(c) == 3 |
| 122 | +@test c.data == zeros(3, 2) |
| 123 | +oldtype = invalidate_struct(RedefStruct, :C) |
| 124 | +ex = quote |
| 125 | + struct C{T,N,A<:AbstractArray{T,N}} |
| 126 | + data::A |
| 127 | + |
| 128 | + function C{T,N,A}(data::AbstractArray) where {T,N,A} |
| 129 | + return new{T,N,A}(data) |
| 130 | + end |
| 131 | + end |
| 132 | +end |
| 133 | +Core.eval(RedefStruct, ex) |
| 134 | +c32 = RedefStruct.C{Float32,2,Array{Float32,2}}([0.1 0.2; 0.3 0.4]) |
| 135 | +@test c32.data isa Array{Float32,2} |
| 136 | +@test_throws MethodError RedefStruct.C([0.1 0.2; 0.3 0.4]) # outer constructor is not yet defined |
| 137 | +Core.eval(RedefStruct, :(C(data::AbstractArray{T,N}) where {T,N} = C{T,N,typeof(data)}(data))) |
| 138 | +c64 = RedefStruct.C([0.1 0.2; 0.3 0.4]) |
| 139 | +@test c64.data isa Array{Float64,2} |
| 140 | +@test_throws MethodError RedefStruct.fC(c32) |
| 141 | +cc = RedefStruct.gC((3,2)) |
| 142 | +@test cc.data == zeros(3, 2) && isa(cc.data, Matrix{Float64}) |
0 commit comments