What causes the stack overflow when default discriminants are used in this variant record?
procedure Example is
type Option is (A, B);
-- type Test (X : Option; Y : Natural) is -- OK
type Test (X : Option := A; Y : Natural := 0) is -- raised STORAGE_ERROR : stack overflow
record
case X is
when A =>
null;
when B =>
V : String (1 .. Y);
end case;
end record;
Z : Test := (X => A, Y => 0);
begin
null;
end Example;
It’s because when you specify a default discriminant, the compiler has to figure out a way to ensure it can build every variant of the record in a consistent size (so it can be used in arrays and definite types).
GNAT went the route of saying all variants have the size of the largest possible variant. In your case that is the variant where Y is Natural’Last, making a really really large array (hence the storage error).
Some other compilers instead go the route of doing heap based allocation instead so every variant just holds a pointer. JanusAda comes to mind for this method.
You’ll ether need to constrain Y to a much smaller range, or forgo the default descriminant method and try a different approach to your problem.
One way to work around the issue that jere described, you can use a subtype with a smaller range to limit the maximum length of the string, and therefore the maximum discriminated object size. For example, to limit the maximum length to 128 characters:
procedure Example is
type Option is (A, B);
subtype Length_Number is Natural range 0 .. 128;
type Test (X : Option := A; Y : Length_Number := 0) is
record
case X is
when A =>
null;
when B =>
V : String (1 .. Y);
end case;
end record;
Z : Test := (X => A, Y => 0);
begin
null;
end Example;
Others have explained why this happens with some compilers, and how to get a variant record that doesn’t overflow your stack.
However, what you appear to be trying to do is have a record with a variable-length string in it. A way to do this is to use an abstraction that implements a variable-length string. These come in two flavors:
Bounded
Unbounded
Both are provided as part of the standard library.
Bounded strings have a user-specified maximum length which is allocated on the stack. Often, only part of the allocated space is used. This is effectively what you’re doing when using GNAT.
Unbounded Strings have no user-specified maximum length and are usually allocated on the heap. Memory management is needed and usually provided by the abstraction.
Either approach will likely be clearer and easier to use than rolling your own with a record discriminant.