(If it matters, alr tells me that this is gnat_external=12.3.1; notice the pragma)
I was recently in a situation along these lines:
pragma Ada_2022;
-- ...
type Enum is (A, B, C);
type Disc_Rec (Kind : Enum) is record
case Kind is
when A => Field : Natural;
when others => null;
end case;
end record;
-- ... in what follows, `Thing` is of type `constant Enum` and `Value` is of type
D : Disc_Rec := (
case Thing is
when A => Disc_Rec'(Kind => A, Field => Value),
when B | C => Disc_Rec'(Kind => Thing) -- LINE 37
);
gnat tells me:
test_enum.adb:37:48: error: no single variant is associated with all values of the subtype of discriminant value "Kind"
Column 47 lands on Thing.
I understand the message, but I’d have thought it clear that since Thing has the type B or C in line 37, Thing has all values of the subtype that matter in this branch. Indeed, if Disc_Rec does not have the case statement that defines Field, and I remove the assignment to Field in line 36, the compiler sings happily. So the problem doesn’t seem to be Thing so much as that the optional Field… which isn’t needed in this branch.
Is there a way to make something like this work? My current workaround handles the branches explicitly (when A => ... , when B => ..., when C => ...), but In a practical case, Kind could have a lot of variants.
Sometimes you can use a subtype for things like this
subtype Non_A is Enum range B .. C;
...
when B | C => Disc_Rec'(Kind => Non_A (Thing) )
(not tested)
In the general case, you may need a function
function New_Disc_Rec (Disc : in Enum; Value : in Natural := 0) return Disc_Rec is
Result : Disc_Rec (Kind => Disc);
begin -- New_Disc_Rec
case Disc is
when A =>
Result.Field := Value;
when others =>
null;
end case;
return Result;
end New_Disc_Rec;
D : Disc_Rec := New_Disc_Rec (Thing, Value);
Note that you have to declare a variable to return (an extended return will also work), as the discriminant in an aggregate must be static, and a parameter never is.
You can also change up your case statement to use “static” values, which means more case statement lines but satisfies the requirement that a discriminant record aggregate must be static.
type Enum is (A, B, C);
type Disc_Rec (Kind : Enum) is record
case Kind is
when A => Field : Natural;
when others => null;
end case;
end record;
Thing : constant Enum := A;
Value : constant Natural := 0;
D : Disc_Rec(Thing) := (
case Thing is
when A => (Kind => A, Field => Value),
when B => (Kind => B),
when C => (Kind => C)
);
If you go the function route as Jeffrey suggests (I also prefer the function route), I would definitely recommend the extended return syntax (not shown above) as it is cleaner to read for a lot of folks and the compiler can also optimize it easier.