diff --git a/src/base.jl b/src/base.jl index d9d6c435..514efc9c 100644 --- a/src/base.jl +++ b/src/base.jl @@ -250,6 +250,7 @@ possible(::typeof(_setproperty!), x, ::Any, ::Any) = ismutablestruct(x) materialize!!(dest, x) """ materialize!!(dest, x) = may(materialize!, dest, x) +# TODO: maybe instantiate `x` and be aware of `x`'s style pure(::typeof(materialize!)) = NoBang.materialize possible(::typeof(materialize!), x, ::Any) = ismutable(x) diff --git a/src/core.jl b/src/core.jl index 71fb9d9f..f8fcb8cc 100644 --- a/src/core.jl +++ b/src/core.jl @@ -44,3 +44,6 @@ ismutablestruct(::Type{<:NamedTuple}) = false # trymutate(::typeof(push!)) = push!! # trymutate(::typeof(append!)) = append!! + +struct Undefined end +ismutable(::Undefined) = false diff --git a/src/macro.jl b/src/macro.jl index 622d4543..72202815 100644 --- a/src/macro.jl +++ b/src/macro.jl @@ -14,7 +14,15 @@ julia> @! push!(empty!((0, 1)), 2, 3) macro !(expr) foldexpr(expr) do x if Meta.isexpr(x, :call) + # TODO: handle `x .* y` return Expr(:call, Expr(:call, _maybb, x.args[1]), x.args[2:end]...) + elseif Meta.isexpr(x, :.=) && x.args[1] isa Symbol + @assert length(x.args) == 2 + lhs, rhs = x.args + return :($lhs = $materialize!!( + $Base.@isdefined($lhs) ? $lhs : $(Undefined()), + $rhs, + )) end return x end |> esc diff --git a/test/test_macro.jl b/test/test_macro.jl index 829d37af..7091a95d 100644 --- a/test/test_macro.jl +++ b/test/test_macro.jl @@ -7,4 +7,29 @@ include("preamble.jl") @test (@! push!(empty!((0, 1)), 2, 3)) == (2, 3) end +@testset ".=" begin + @test !@isdefined y + @test begin + x = SVector(1, 2) + @! y .= (*).(x, 2) + end === SVector(2, 4) + + @test !@isdefined y + @test begin + x = SVector(1, 2) + y = SVector(0, 0) + @! y .= (*).(x, 2) + end === SVector(2, 4) + + let x = [1, 2] + y = [0, 0] + @test (@! y .= (*).(x, 2))::Vector{Int} == [2, 4] + end + + let x = [1, 2] + y = SVector(0, 0) + @test (@! y .= (*).(x, 2))::Vector{Int} == [2, 4] + end +end + end # module