Skip to content

Commit df33941

Browse files
committed
implement setproperty! for modules
This replaces #44137. As discussed on triage, instead of supporting modules in `setfield!`, this adds two new builtins `getglobal` and `setglobal!` explicitly for reading and modifying module bindings. We should probably consider `getfield(::Module, ::Symbol)` to be soft-deprecated, but I don't think we want to add any warnings since that will likely just annoy people.
1 parent 1ad2396 commit df33941

File tree

13 files changed

+216
-93
lines changed

13 files changed

+216
-93
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Julia v1.9 Release Notes
44
New language features
55
---------------------
66

7+
* It is now possible to assign to bindings in another module using `setproperty!(::Module, ::Symbol, x)`. ([#44137])
78

89
Language changes
910
----------------

base/Base.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ macro noinline() Expr(:meta, :noinline) end
2828
# Try to help prevent users from shooting them-selves in the foot
2929
# with ambiguities by defining a few common and critical operations
3030
# (and these don't need the extra convert code)
31-
getproperty(x::Module, f::Symbol) = (@inline; getfield(x, f))
32-
setproperty!(x::Module, f::Symbol, v) = setfield!(x, f, v) # to get a decent error
31+
getproperty(x::Module, f::Symbol) = (@inline; Core.getglobal(x, f))
3332
getproperty(x::Type, f::Symbol) = (@inline; getfield(x, f))
3433
setproperty!(x::Type, f::Symbol, v) = error("setfield! fields of Types should not be changed")
3534
getproperty(x::Tuple, f::Int) = (@inline; getfield(x, f))
@@ -40,8 +39,12 @@ setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f),
4039

4140
dotgetproperty(x, f) = getproperty(x, f)
4241

43-
getproperty(x::Module, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order))
44-
setproperty!(x::Module, f::Symbol, v, order::Symbol) = setfield!(x, f, v, order) # to get a decent error
42+
getproperty(x::Module, f::Symbol, order::Symbol) = (@inline; Core.getglobal(x, f, order))
43+
function setproperty!(x::Module, f::Symbol, v, order::Symbol=:monotonic)
44+
@inline
45+
val::Core.get_binding_type(x, f) = v
46+
return Core.setglobal!(x, f, val, order)
47+
end
4548
getproperty(x::Type, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order))
4649
setproperty!(x::Type, f::Symbol, v, order::Symbol) = error("setfield! fields of Types should not be changed")
4750
getproperty(x::Tuple, f::Int, order::Symbol) = (@inline; getfield(x, f, order))

base/compiler/tfuncs.jl

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,37 @@ function arraysize_nothrow(argtypes::Vector{Any})
480480
return false
481481
end
482482

483+
struct MemoryOrder x::Cint end
484+
const MEMORY_ORDER_UNSPECIFIED = MemoryOrder(-2)
485+
const MEMORY_ORDER_INVALID = MemoryOrder(-1)
486+
const MEMORY_ORDER_NOTATOMIC = MemoryOrder(0)
487+
const MEMORY_ORDER_UNORDERED = MemoryOrder(1)
488+
const MEMORY_ORDER_MONOTONIC = MemoryOrder(2)
489+
const MEMORY_ORDER_CONSUME = MemoryOrder(3)
490+
const MEMORY_ORDER_ACQUIRE = MemoryOrder(4)
491+
const MEMORY_ORDER_RELEASE = MemoryOrder(5)
492+
const MEMORY_ORDER_ACQ_REL = MemoryOrder(6)
493+
const MEMORY_ORDER_SEQ_CST = MemoryOrder(7)
494+
495+
function get_atomic_order(order::Symbol, loading::Bool, storing::Bool)
496+
if order === :not_atomic
497+
return MEMORY_ORDER_NOTATOMIC
498+
elseif order === :unordered && (loading storing)
499+
return MEMORY_ORDER_UNORDERED
500+
elseif order === :monotonic && (loading | storing)
501+
return MEMORY_ORDER_MONOTONIC
502+
elseif order === :acquire && loading
503+
return MEMORY_ORDER_ACQUIRE
504+
elseif order === :release && storing
505+
return MEMORY_ORDER_RELEASE
506+
elseif order === :acquire_release && (loading & storing)
507+
return MEMORY_ORDER_ACQ_REL
508+
elseif order === :sequentially_consistent
509+
return MEMORY_ORDER_SEQ_CST
510+
end
511+
return MEMORY_ORDER_INVALID
512+
end
513+
483514
function pointer_eltype(@nospecialize(ptr))
484515
a = widenconst(ptr)
485516
if !has_free_typevars(a)
@@ -1704,6 +1735,8 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ
17041735
return true
17051736
end
17061737
return false
1738+
elseif f === Core.getglobal
1739+
return getglobal_nothrow(argtypes)
17071740
elseif f === Core.get_binding_type
17081741
return length(argtypes) == 2
17091742
elseif f === donotdelete
@@ -1773,16 +1806,20 @@ function builtin_effects(f::Builtin, argtypes::Vector{Any}, rt)
17731806
# InferenceState.
17741807
nothrow = getfield_nothrow(argtypes[2], argtypes[3], true)
17751808
ipo_consistent &= nothrow
1776-
end
1809+
else
1810+
nothrow = isvarargtype(argtypes[end]) ? false :
1811+
builtin_nothrow(f, argtypes[2:end], rt)
1812+
end
1813+
effect_free = f === isdefined
1814+
elseif f === Core.getglobal && length(argtypes) >= 3
1815+
nothrow = effect_free = getglobal_nothrow(argtypes[2:end])
1816+
ipo_consistent = nothrow && isconst((argtypes[2]::Const).val, (argtypes[3]::Const).val)
1817+
effect_free = nothrow && isbindingresolved((argtypes[2]::Const).val, (argtypes[3]::Const).val)
17771818
else
17781819
ipo_consistent = contains_is(_CONSISTENT_BUILTINS, f)
1820+
effect_free = contains_is(_EFFECT_FREE_BUILTINS, f) || contains_is(_PURE_BUILTINS, f)
1821+
nothrow = isvarargtype(argtypes[end]) ? false : builtin_nothrow(f, argtypes[2:end], rt)
17791822
end
1780-
# If we computed nothrow above for getfield, no need to repeat the procedure here
1781-
if !nothrow
1782-
nothrow = isvarargtype(argtypes[end]) ? false :
1783-
builtin_nothrow(f, argtypes[2:end], rt)
1784-
end
1785-
effect_free = contains_is(_EFFECT_FREE_BUILTINS, f) || contains_is(_PURE_BUILTINS, f)
17861823

17871824
return Effects(
17881825
ipo_consistent ? ALWAYS_TRUE : ALWAYS_FALSE,
@@ -2029,17 +2066,50 @@ function typename_static(@nospecialize(t))
20292066
return isType(t) ? _typename(t.parameters[1]) : Core.TypeName
20302067
end
20312068

2069+
function global_order_nothrow(@nospecialize(o), loading::Bool, storing::Bool)
2070+
o isa Const || return false
2071+
sym = o.val
2072+
if sym isa Symbol
2073+
order = get_atomic_order(sym, loading, storing)
2074+
return order !== MEMORY_ORDER_INVALID && order !== MEMORY_ORDER_NOTATOMIC
2075+
end
2076+
return false
2077+
end
2078+
function getglobal_nothrow(argtypes::Vector{Any})
2079+
2 length(argtypes) 3 || return false
2080+
if length(argtypes) == 3
2081+
global_order_nothrow(o, true, false) || return false
2082+
end
2083+
M, s = argtypes
2084+
if M isa Const && s isa Const
2085+
M, s = M.val, s.val
2086+
if M isa Module && s isa Symbol
2087+
return isdefined(M, s)
2088+
end
2089+
end
2090+
return false
2091+
end
2092+
function getglobal_tfunc(@nospecialize(M), @nospecialize(s), @nospecialize(_=:monotonic))
2093+
if get_binding_type_effect_free(M, s)
2094+
return abstract_eval_global((M::Const).val, (s::Const).val)
2095+
end
2096+
return Any
2097+
end
2098+
add_tfunc(Core.getglobal, 2, 3, getglobal_tfunc, 1)
2099+
add_tfunc(Core.setglobal!, 3, 4, (M, s, v, _=:monotonic) -> (@nospecialize; v), 1)
2100+
20322101
function get_binding_type_effect_free(@nospecialize(M), @nospecialize(s))
2033-
if M isa Const && widenconst(M) === Module &&
2034-
s isa Const && widenconst(s) === Symbol
2035-
return ccall(:jl_binding_type, Any, (Any, Any), M.val, s.val) !== nothing
2102+
if M isa Const && s isa Const
2103+
M, s = M.val, s.val
2104+
if M isa Module && s isa Symbol
2105+
return ccall(:jl_binding_type, Any, (Any, Any), M, s) !== nothing
2106+
end
20362107
end
20372108
return false
20382109
end
20392110
function get_binding_type_tfunc(@nospecialize(M), @nospecialize(s))
20402111
if get_binding_type_effect_free(M, s)
2041-
@assert M isa Const && s isa Const
2042-
return Const(Core.get_binding_type(M.val, s.val))
2112+
return Const(Core.get_binding_type((M::Const).val, (s::Const).val))
20432113
end
20442114
return Type
20452115
end

base/docs/basedocs.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,9 @@ The syntax `a.b = c` calls `setproperty!(a, :b, c)`.
27312731
The syntax `@atomic order a.b = c` calls `setproperty!(a, :b, c, :order)`
27322732
and the syntax `@atomic a.b = c` calls `getproperty(a, :b, :sequentially_consistent)`.
27332733
2734+
!!! compat "Julia 1.8"
2735+
`setproperty!` on modules requires at least Julia 1.8.
2736+
27342737
See also [`setfield!`](@ref Core.setfield!),
27352738
[`propertynames`](@ref Base.propertynames) and
27362739
[`getproperty`](@ref Base.getproperty).

doc/src/manual/variables-and-scoping.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,6 @@ julia> module D
9191
b = a # errors as D's global scope is separate from A's
9292
end;
9393
ERROR: UndefVarError: a not defined
94-
95-
julia> module E
96-
import ..A # make module A available
97-
A.a = 2 # throws below error
98-
end;
99-
ERROR: cannot assign variables in other modules
10094
```
10195

10296
If a top-level expression contains a variable declaration with keyword `local`,

doc/src/manual/variables.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ julia> pi
8181
π = 3.1415926535897...
8282
8383
julia> pi = 3
84-
ERROR: cannot assign a value to variable MathConstants.pi from module Main
84+
ERROR: cannot assign a value to imported variable MathConstants.pi from module Main
8585
8686
julia> sqrt(100)
8787
10.0
8888
8989
julia> sqrt = 4
90-
ERROR: cannot assign a value to variable Base.sqrt from module Main
90+
ERROR: cannot assign a value to imported variable Base.sqrt from module Main
9191
```
9292

9393
## [Allowed Variable Names](@id man-allowed-variable-names)

src/builtin_proto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ JL_CALLABLE(jl_f__equiv_typedef);
7070
JL_CALLABLE(jl_f_get_binding_type);
7171
JL_CALLABLE(jl_f_set_binding_type);
7272
JL_CALLABLE(jl_f_donotdelete);
73+
JL_CALLABLE(jl_f_getglobal);
74+
JL_CALLABLE(jl_f_setglobal);
7375

7476
#ifdef __cplusplus
7577
}

0 commit comments

Comments
 (0)