Skip to content

Commit 6fa4af5

Browse files
authored
mpfr: prevent changing precision (#56049)
Changing precision requires reallocating the data field, which is better done by making a new BigFloat (since they are conceptually immutable anyways). Also do a bit a cleanup while here. Closes #56044
1 parent ce83853 commit 6fa4af5

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

base/mpfr.jl

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -142,22 +142,28 @@ struct BigFloat <: AbstractFloat
142142

143143
# Not recommended for general use:
144144
# used internally by, e.g. deepcopy
145-
global _BigFloat(d::Memory{Limb}) = new(d)
145+
global function _BigFloat(d::Memory{Limb})
146+
Base.unsafe_convert(Ref{BigFloat}, BigFloatData(d)) # force early initialization of pointer field of z.d
147+
return new(d)
148+
end
146149

147150
function BigFloat(; precision::Integer=_precision_with_base_2(BigFloat))
148151
precision < 1 && throw(DomainError(precision, "`precision` cannot be less than 1."))
149152
nb = ccall((:mpfr_custom_get_size,libmpfr), Csize_t, (Clong,), precision)
150153
nl = (nb + offset_p + sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this
151154
d = Memory{Limb}(undef, nl % Int)
152155
# ccall-based version, inlined below
153-
z = _BigFloat(d) # initialize to +NAN
154156
#ccall((:mpfr_custom_init,libmpfr), Cvoid, (Ptr{Limb}, Clong), BigFloatData(d), prec) # currently seems to be a no-op in mpfr
155157
#NAN_KIND = Cint(0)
156158
#ccall((:mpfr_custom_init_set,libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, BigFloatData(d))
157-
z.prec = Clong(precision)
158-
z.sign = one(Cint)
159-
z.exp = mpfr_special_exponent_nan
160-
return z
159+
p = Base.unsafe_convert(Ptr{Limb}, d)
160+
GC.@preserve d begin # initialize to +NAN
161+
unsafe_store!(Ptr{Clong}(p) + offset_prec, Clong(precision))
162+
unsafe_store!(Ptr{Cint}(p) + offset_sign, one(Cint))
163+
unsafe_store!(Ptr{Clong}(p) + offset_exp, mpfr_special_exponent_nan)
164+
unsafe_store!(Ptr{Ptr{Limb}}(p) + offset_d, p + offset_p)
165+
end
166+
return new(d)
161167
end
162168
end
163169

@@ -186,16 +192,16 @@ end
186192
end
187193
end
188194

195+
# While BigFloat (like all Numbers) is considered immutable, for practical reasons
196+
# of writing the algorithms on it we allow mutating sign, exp, and the contents of d
189197
@inline function Base.setproperty!(x::BigFloat, s::Symbol, v)
190198
d = getfield(x, :d)
191199
p = Base.unsafe_convert(Ptr{Limb}, d)
192-
if s === :prec
193-
return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_prec, v)
194-
elseif s === :sign
200+
if s === :sign
195201
return GC.@preserve d unsafe_store!(Ptr{Cint}(p) + offset_sign, v)
196202
elseif s === :exp
197203
return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_exp, v)
198-
#elseif s === :d # not mutable
204+
#elseif s === :d || s === :prec # not mutable
199205
else
200206
return throw(FieldError(x, s))
201207
end
@@ -208,7 +214,11 @@ Base.cconvert(::Type{Ref{BigFloat}}, x::BigFloat) = x.d # BigFloatData is the Re
208214
function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::BigFloatData)
209215
d = getfield(x, :d)
210216
p = Base.unsafe_convert(Ptr{Limb}, d)
211-
GC.@preserve d unsafe_store!(Ptr{Ptr{Limb}}(p) + offset_d, p + offset_p, :monotonic) # :monotonic ensure that TSAN knows that this isn't a data race
217+
dptrptr = Ptr{Ptr{Limb}}(p) + offset_d
218+
dptr = p + offset_p
219+
GC.@preserve d if unsafe_load(dptrptr, :monotonic) != dptr # make sure this pointer value was recomputed after any deserialization or copying
220+
unsafe_store!(dptrptr, dptr, :monotonic) # :monotonic ensure that TSAN knows that this isn't a data race
221+
end
212222
return Ptr{BigFloat}(p)
213223
end
214224
Base.unsafe_convert(::Type{Ptr{Limb}}, fd::BigFloatData) = Base.unsafe_convert(Ptr{Limb}, getfield(fd, :d)) + offset_p

0 commit comments

Comments
 (0)