@@ -633,6 +633,10 @@ expression. The default argument is supplied by declaring fields of the form `fi
633
633
default` or `field = default`. If no default is provided then the keyword argument becomes
634
634
a required keyword argument in the resulting type constructor.
635
635
636
+ Inner constructors can still be defined, but at least one should accept arguments in the
637
+ same form as the default inner constructor (i.e. one positional argument per field) in
638
+ order to function correctly with the keyword outer constructor.
639
+
636
640
# Examples
637
641
```jldoctest
638
642
julia> Base.@kwdef struct Foo
@@ -652,51 +656,82 @@ Stacktrace:
652
656
"""
653
657
macro kwdef (expr)
654
658
expr = macroexpand (__module__, expr) # to expand @static
659
+ expr isa Expr && expr. head == :struct || error (" Invalid usage of @kwdef" )
655
660
T = expr. args[2 ]
656
- params_ex = Expr (:parameters )
657
- call_ex = Expr (:call , T)
658
- _kwdef! (expr. args[3 ], params_ex, call_ex)
659
- ret = quote
660
- Base. @__doc__ ($ (esc (expr)))
661
+ if T isa Expr && T. head == :< :
662
+ T = T. args[1 ]
661
663
end
664
+
665
+ params_ex = Expr (:parameters )
666
+ call_args = Any[]
667
+
668
+ _kwdef! (expr. args[3 ], params_ex. args, call_args)
662
669
# Only define a constructor if the type has fields, otherwise we'll get a stack
663
670
# overflow on construction
664
671
if ! isempty (params_ex. args)
665
- push! (ret. args, :($ (esc (Expr (:call , T, params_ex))) = $ (esc (call_ex))))
672
+ if T isa Symbol
673
+ kwdefs = :(($ (esc (T)))($ params_ex) = ($ (esc (T)))($ (call_args... )))
674
+ elseif T isa Expr && T. head == :curly
675
+ # if T == S{A<:AA,B<:BB}, define two methods
676
+ # S(...) = ...
677
+ # S{A,B}(...) where {A<:AA,B<:BB} = ...
678
+ S = T. args[1 ]
679
+ P = T. args[2 : end ]
680
+ Q = [U isa Expr && U. head == :< : ? U. args[1 ] : U for U in P]
681
+ SQ = :($ S{$ (Q... )})
682
+ kwdefs = quote
683
+ ($ (esc (S)))($ params_ex) = ($ (esc (S)))($ (call_args... ))
684
+ ($ (esc (SQ)))($ params_ex) where {$ (esc .(P)... )} =
685
+ ($ (esc (SQ)))($ (call_args... ))
686
+ end
687
+ else
688
+ error (" Invalid usage of @kwdef" )
689
+ end
690
+ else
691
+ kwdefs = nothing
692
+ end
693
+ quote
694
+ Base. @__doc__ ($ (esc (expr)))
695
+ $ kwdefs
666
696
end
667
- ret
668
697
end
669
698
670
699
# @kwdef helper function
671
700
# mutates arguments inplace
672
- function _kwdef! (blk, params_ex, call_ex )
701
+ function _kwdef! (blk, params_args, call_args )
673
702
for i in eachindex (blk. args)
674
703
ei = blk. args[i]
675
- if isa (ei, Symbol)
676
- push! (params_ex. args, ei)
677
- push! (call_ex. args, ei)
678
- elseif ! isa (ei, Expr)
679
- continue
680
- elseif ei. head == :(= )
681
- # var::Typ = defexpr
682
- dec = ei. args[1 ] # var::Typ
683
- if isa (dec, Expr) && dec. head == :(:: )
684
- var = dec. args[1 ]
685
- else
686
- var = dec
704
+ if ei isa Symbol
705
+ # var
706
+ push! (params_args, ei)
707
+ push! (call_args, ei)
708
+ elseif ei isa Expr
709
+ if ei. head == :(= )
710
+ lhs = ei. args[1 ]
711
+ if lhs isa Symbol
712
+ # var = defexpr
713
+ var = lhs
714
+ elseif lhs isa Expr && lhs. head == :(:: ) && lhs. args[1 ] isa Symbol
715
+ # var::T = defexpr
716
+ var = lhs. args[1 ]
717
+ else
718
+ # something else, e.g. inline inner constructor
719
+ # F(...) = ...
720
+ continue
721
+ end
722
+ defexpr = ei. args[2 ] # defexpr
723
+ push! (params_args, Expr (:kw , var, esc (defexpr)))
724
+ push! (call_args, var)
725
+ blk. args[i] = lhs
726
+ elseif ei. head == :(:: ) && ei. args[1 ] isa Symbol
727
+ # var::Typ
728
+ var = ei. args[1 ]
729
+ push! (params_args, var)
730
+ push! (call_args, var)
731
+ elseif ei. head == :block
732
+ # can arise with use of @static inside type decl
733
+ _kwdef! (ei, params_args, call_args)
687
734
end
688
- def = ei. args[2 ] # defexpr
689
- push! (params_ex. args, Expr (:kw , var, def))
690
- push! (call_ex. args, var)
691
- blk. args[i] = dec
692
- elseif ei. head == :(:: )
693
- dec = ei # var::Typ
694
- var = dec. args[1 ] # var
695
- push! (params_ex. args, var)
696
- push! (call_ex. args, var)
697
- elseif ei. head == :block
698
- # can arise with use of @static inside type decl
699
- _kwdef! (ei, params_ex, call_ex)
700
735
end
701
736
end
702
737
blk
0 commit comments