@@ -457,15 +457,14 @@ _computeproperty(o, name, indices...; __status__=nothing, kwargs...) = begin
457457 compute_property (o, vname, indices... ; _status_kw... , kwargs... )
458458 end
459459 catch e
460- e isa PropertyComputationError && rethrow ()
461460 kw_tuple = isempty (kwargs) ? () : Tuple (pairs (kwargs))
462461 bt = catch_backtrace ()
463462 throw (PropertyComputationError (
464463 string (typeof (o). name. name),
465464 name,
466465 indices,
467466 kw_tuple,
468- (e, bt), # store exception + backtrace from the actual throw site
467+ (e, bt),
469468 ))
470469 end
471470end
@@ -738,6 +737,69 @@ dynamicstruct(expr; docstring=nothing, cache_type=:serial) = begin
738737 Meta. isexpr (type, :(< :)) && (type = type. args[1 ])
739738 Meta. isexpr (type, :(curly)) && (type = type. args[1 ])
740739 @assert body. head == :block
740+ # --- Extract inline struct definitions ---
741+ # Collect parent property names (excluding inline structs themselves)
742+ parent_props = Symbol[]
743+ for arg in body. args
744+ arg isa Expr || continue
745+ a = arg
746+ while Meta. isexpr (a, :macrocall ); a = a. args[end ]; end
747+ # Skip inline structs (both forms)
748+ Meta. isexpr (a, :struct ) && continue
749+ Meta. isexpr (a, :(= )) && Meta. isexpr (a. args[2 ], :struct ) && continue
750+ lhs = if Meta. isexpr (a, :(= ))
751+ a. args[1 ]
752+ else
753+ a # fixed field: bare symbol or typed symbol
754+ end
755+ Meta. isexpr (lhs, (:call , :ref )) && (lhs = lhs. args[1 ])
756+ Meta. isexpr (lhs, :(:: )) && (lhs = lhs. args[1 ])
757+ lhs isa Symbol && push! (parent_props, lhs)
758+ end
759+ extracted_structs = Expr[]
760+ for (i, arg) in enumerate (body. args)
761+ arg isa Expr || continue
762+ prop_name = nothing
763+ child_struct = nothing
764+ # Form 1: prop = struct Name ... end
765+ if Meta. isexpr (arg, :(= )) && Meta. isexpr (arg. args[2 ], :struct )
766+ prop_name = arg. args[1 ]
767+ child_struct = arg. args[2 ]
768+ # Form 2: struct Name ... end (bare)
769+ elseif Meta. isexpr (arg, :struct )
770+ child_struct = arg
771+ end
772+ isnothing (child_struct) && continue
773+ child_name = child_struct. args[2 ]
774+ isnothing (prop_name) && (prop_name = child_name)
775+ # Rename child struct to Parent_Child to avoid kwarg shadowing
776+ gen_name = Symbol (type, " _" , child_name)
777+ child_struct. args[2 ] = gen_name
778+ # Collect child's own property names to avoid collision
779+ child_props = Set {Symbol} ()
780+ for ca in child_struct. args[3 ]. args
781+ ca isa Expr || continue
782+ ca2 = ca
783+ while Meta. isexpr (ca2, :macrocall ); ca2 = ca2. args[end ]; end
784+ Meta. isexpr (ca2, :(= )) || continue
785+ clhs = ca2. args[1 ]
786+ Meta. isexpr (clhs, (:call , :ref )) && (clhs = clhs. args[1 ])
787+ Meta. isexpr (clhs, :(:: )) && (clhs = clhs. args[1 ])
788+ clhs isa Symbol && push! (child_props, clhs)
789+ end
790+ # Prepend __parent__ and forwarded properties to child body
791+ child_body = child_struct. args[3 ]
792+ forwarded = [pp for pp in parent_props if ! (pp in child_props)]
793+ prepend = Expr[]
794+ push! (prepend, :(__parent__ = nothing ))
795+ if ! isempty (forwarded)
796+ push! (prepend, :($ (Expr (:tuple , Expr (:parameters , forwarded... ))) = __parent__))
797+ end
798+ child_body. args = vcat (prepend, child_body. args)
799+ push! (extracted_structs, child_struct)
800+ # Replace with property assignment
801+ body. args[i] = :($ prop_name = $ gen_name (; __parent__= __self__))
802+ end
741803 lnn = nothing
742804 doc = nothing
743805 docs = []
@@ -867,7 +929,15 @@ dynamicstruct(expr; docstring=nothing, cache_type=:serial) = begin
867929 )
868930 ))
869931 ))
870- esc (Expr (:block ,
932+ result = Expr (:block )
933+ # Prepend extracted inline child structs (processed recursively)
934+ for s in extracted_structs
935+ child_result = dynamicstruct (s)
936+ # Unwrap esc() — parent handles escaping
937+ @assert Meta. isexpr (child_result, :escape )
938+ push! (result. args, child_result. args[1 ])
939+ end
940+ push! (result. args, Expr (:block ,
871941 :(@doc $ docstring $ struct_expr),
872942 quote
873943 $ Base. hasproperty (__self__:: $type , name:: Symbol ) = name in $ (Tuple (keys (properties)))
@@ -911,6 +981,7 @@ dynamicstruct(expr; docstring=nothing, cache_type=:serial) = begin
911981 # directly in getorcomputeproperty (via meta check), so no zero-arg
912982 # compute_property methods are needed here.
913983 ))
984+ esc (result)
914985end
915986
916987# Replace only the top-level LineNumberNodes in a block, leaving nested ones intact.
@@ -1116,17 +1187,15 @@ end
11161187function Base. showerror (io:: IO , e:: PropertyComputationError )
11171188 key = _format_property_key (e. property, e. indices, e. kwargs)
11181189 print (io, " PropertyComputationError: computing `$key ` on $(e. type_name) \n " )
1190+ cause_err = _cause_error (e)
1191+ cause_bt = e. cause isa Tuple && length (e. cause) >= 2 ? e. cause[2 ] : nothing
11191192 print (io, " Caused by: " )
1120- showerror (io, unwrap_error (e))
1121- end
1122-
1123- function Base. showerror (io:: IO , e:: PropertyComputationError , bt; kwargs... )
1124- try
1125- showerror (io, e)
1126- catch internal_err
1127- print (io, " PropertyComputationError (display failed: " )
1128- showerror (io, internal_err)
1129- print (io, " )" )
1193+ if cause_err isa PropertyComputationError
1194+ showerror (io, cause_err)
1195+ elseif ! isnothing (cause_bt)
1196+ showerror (io, cause_err, cause_bt)
1197+ else
1198+ showerror (io, cause_err)
11301199 end
11311200end
11321201
0 commit comments