diff --git a/Project.toml b/Project.toml index 30a618092..b8abe79e4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ChainRulesCore" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.26.7" +version = "1.27.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/tangent_types/structural_tangent.jl b/src/tangent_types/structural_tangent.jl index 94a0e4e48..0e5a4d3dc 100644 --- a/src/tangent_types/structural_tangent.jl +++ b/src/tangent_types/structural_tangent.jl @@ -405,6 +405,14 @@ function Base.indexed_iterate(tangent::Tangent{P,<:Tuple}, i::Int, state=1) wher return Base.indexed_iterate(backing(tangent), i, state) end +# Whether a structural tangent should broadcast as a container (e.g. a tuple broadcast) or +# as a scalar (e.g. a 0-dim broadcast with a struct-valued output) depends on the broadcast, +# and in general can't be determined from the tangent alone (e.g. for `Tangent{Any}`) +function Base.Broadcast.broadcastable(::StructuralTangent) + msg = "broadcasting over structural tangents is not supported; use `map` or arithmetic" + return throw(ArgumentError(msg)) +end + function Base.map(f, tangent::Tangent{P,<:Tuple}) where {P} vals::Tuple = map(f, backing(tangent)) return Tangent{P,typeof(vals)}(vals) diff --git a/test/tangent_types/structural_tangent.jl b/test/tangent_types/structural_tangent.jl index c0b26217e..746c55849 100644 --- a/test/tangent_types/structural_tangent.jl +++ b/test/tangent_types/structural_tangent.jl @@ -140,6 +140,15 @@ end @inferred _unpack2tuple(ctup) @test _unpack2tuple(ctup) === (2.0, 3) + # Test broadcasting is not supported + @test_throws ArgumentError ctup .* 2 + @test_throws ArgumentError Tangent{Any}(1.0, [2.0]) .* 2 + @test_throws ArgumentError ctup .+ [1, 2] + @test_throws ArgumentError Tangent{Foo}(; y=2.0, x=1.0) .* 2 + @test_throws ArgumentError Tangent{Dict}(Dict(:a => 1.0)) .* 2 + @test_throws ArgumentError MutableTangent{MFoo}(; x=1.5, y=1.5) .* 2 + @test_throws ArgumentError @thunk(ctup) .* 2 + # Test getproperty is inferrable _unpacknamedtuple = tangent -> (tangent.x, tangent.y) @inferred _unpacknamedtuple(Tangent{Foo}(; x=2, y=3.0))