diff --git a/Project.toml b/Project.toml index 844289af5..5257e90f8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "DynamicPPL" uuid = "366bfd00-2699-11ea-058f-f148b4cae6d8" -version = "0.17.0" +version = "0.17.1" [deps] AbstractMCMC = "80f14c24-f653-4e6a-9b94-39d6b0f70001" diff --git a/src/compiler.jl b/src/compiler.jl index c7b310f46..b9c7eead9 100644 --- a/src/compiler.jl +++ b/src/compiler.jl @@ -518,16 +518,17 @@ function replace_returns(e::Expr) end if Meta.isexpr(e, :return) - # NOTE: `return` always has an argument. In the case of - # an empty `return`, the lowered expression will be `return nothing`. - # Hence we don't need any special handling for empty returns. - retval_expr = if length(e.args) > 1 - Expr(:tuple, e.args...) - else - e.args[1] + # We capture the original return-value in `retval` and return + # a `Tuple{typeof(retval),typeof(__varinfo__)}`. + # If we don't capture the return-value separately, cases such as + # `return x = 1` will result in `(x = 1, __varinfo__)` which will + # mistakenly attempt to construct a `NamedTuple` (which fails on Julia 1.3 + # and is not our intent). + @gensym retval + return quote + $retval = $(e.args...) + return $retval, __varinfo__ end - - return :(return ($retval_expr, __varinfo__)) end return Expr(e.head, map(replace_returns, e.args)...) diff --git a/test/compiler.jl b/test/compiler.jl index 0544bb5f6..9dc81ff16 100644 --- a/test/compiler.jl +++ b/test/compiler.jl @@ -546,6 +546,13 @@ end end @testset "return value" begin + # Make sure that a return-value of `x = 1` isn't combined into + # an attempt at a `NamedTuple` of the form `(x = 1, __varinfo__)`. + @model empty_model() = return x = 1 + empty_vi = VarInfo() + retval_and_vi = DynamicPPL.evaluate!!(empty_model(), empty_vi, SamplingContext()) + @test retval_and_vi isa Tuple{Int,typeof(empty_vi)} + # Even if the return-value is `AbstractVarInfo`, we should return # a `Tuple` with `AbstractVarInfo` in the second component too. @model demo() = return __varinfo__