diff --git a/src/Parameters.jl b/src/Parameters.jl index dd9a634..f0d8c22 100644 --- a/src/Parameters.jl +++ b/src/Parameters.jl @@ -45,7 +45,7 @@ import Base: @__doc__ import OrderedCollections: OrderedDict using UnPack: @unpack, @pack! -export @with_kw, @with_kw_noshow, type2dict, reconstruct, @unpack, @pack!, @pack, @consts +export @with_kw, @kw_only, @with_kw_noshow, type2dict, reconstruct, @unpack, @pack!, @pack, @consts ## Parser helpers ################# @@ -326,7 +326,7 @@ macro pack_MM(varname) end ``` """ -function with_kw(typedef, mod::Module, withshow=true) +function with_kw(typedef, mod::Module, withshow=true, allow_default=true) if typedef.head==:tuple # named-tuple withshow==false && error("`@with_kw_noshow` not supported for named tuples") return with_kw_nt(typedef, mod) @@ -486,18 +486,27 @@ function with_kw(typedef, mod::Module, withshow=true) push!(args, k) push!(kwargs.args, Expr(:kw,k,w)) end - if length(typparas)>0 - tps = stripsubtypes(typparas) - innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = $tn{$(tps...)}($(args...))) + if allow_default + if length(typparas)>0 + tps = stripsubtypes(typparas) + innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = $tn{$(tps...)}($(args...))) + else + innerc = :($tn($kwargs) = $tn($(args...)) ) + end else - innerc = :($tn($kwargs) = $tn($(args...)) ) + if length(typparas)>0 + tps = stripsubtypes(typparas) + innerc = :( $tn{$(tps...)}($kwargs) where {$(tps...)} = new{$(tps...)}($(args...))) + else + innerc = :($tn($kwargs) = new($(args...)) ) + end end push!(typ.args[3].args, innerc) # Inner positional constructor: only make it if no inner # constructors are user-defined. If one or several are defined, # assume that one has the standard positional signature. - if length(inner_constructors)==0 + if length(inner_constructors)==0 && allow_default if length(typparas)>0 tps = stripsubtypes(typparas) innerc2 = :( $tn{$(tps...)}($(args...)) where {$(tps...)} = new{$(tps...)}($(args...)) ) @@ -679,6 +688,14 @@ macro with_kw(args...) """) end +""" +As `@with_kw` but does not declare a default constructor when no inner +constructor is found. +""" +macro kw_only(typedef) + return esc(with_kw(typedef, __module__, true, false)) +end + """ As `@with_kw` but does not define a `show` method to avoid annoying redefinition warnings. diff --git a/test/runtests.jl b/test/runtests.jl index 79e9286..ba3f5c0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -81,6 +81,34 @@ const TMT1_3 = MT1_3{Int} # Julia bug https://github.com/JuliaLang/julia/issues/ @test "Field r Default: 4\n" == Markdown.plain(REPL.fielddoc(TMT1_3, :r)) @test "A field Default: sdaf\n" == Markdown.plain(REPL.fielddoc(TMT1_3, :c)) +# no positional inner constructor +@kw_only struct KWO + foo + bar +end + +function KWO(args...) + global outer_constructor_called = true + KWO(; foo="foo", bar="bar") +end + +outer_constructor_called = false +kwo = KWO(42) +@test outer_constructor_called == true +@test kwo.foo == "foo" +@test kwo.bar == "bar" + +outer_constructor_called = false +kwo2 = KWO(42, 43) +@test outer_constructor_called == true +@test kwo2.foo == "foo" +@test kwo2.bar == "bar" + +outer_constructor_called = false +kwo3 = KWO(; foo=42, bar=43) +@test outer_constructor_called == false +@test kwo3.foo == 42 +@test kwo3.bar == 43 # parameter-less @with_kw_noshow mutable struct MT2