Title: The Mod Attribute Author: Bob Duff, AdaCore Abstract: T'Mod can be used to convert signed integers to modular integers using modular (wraparound) arithmetic. Ada has two kinds of integer type: signed and modular: type Signed_Integer is range 1..1_000_000; type Modular is mod 2**32; Operations on signed integers can overflow: if the result is outside the base range, Constraint_Error will be raised. The base range of Signed_Integer is the range of Signed_Integer'Base, which is chosen by the compiler, but is likely to be something like -2**31..2**31-1. Operations on modular integers use modular (wraparound) arithmetic. For example: X : Modular := 1; X := - X; Negating X gives -1, which wraps around to 2**32-1, i.e. all-one-bits. But what about a type conversion from signed to modular? Is that a signed operation (so it should overflow) or is it a modular operation (so it should wrap around)? The answer in Ada is the former -- that is, if you try to convert, say, Integer'(-1) to Modular, you will get Constraint_Error: I : Integer := -1; X := Modular (I); -- raises Constraint_Error In Ada 95, the only way to do that conversion is to use Unchecked_Conversion, which is somewhat uncomfortable. Furthermore, if you're trying to convert to a generic formal modular type, how do you know what size of signed integer type to use? Note that Unchecked_Conversion might malfunction if the source and target types are of different sizes. A small feature added to Ada 2005 solves the problem: the Mod attribute: generic type Formal_Modular is mod <>; package Mod_Attribute is function F return Formal_Modular; end Mod_Attribute; package body Mod_Attribute is A_Signed_Integer : Integer := -1; function F return Formal_Modular is begin return Formal_Modular'Mod (A_Signed_Integer); end F; end Mod_Attribute; The Mod attribute will correctly convert from any integer type to a given modular type, using wraparound semantics. Thus, F will return the all-ones bit pattern, for whatever modular type is passed to Formal_Modular.generic type Formal_Modular is mod <>; package Mod_Attribute is function F return Formal_Modular; end Mod_Attribute; package body Mod_Attribute is A_Signed_Integer : Integer := -1; function F return Formal_Modular is begin return Formal_Modular'Mod (A_Signed_Integer); end F; end Mod_Attribute; with Ada.Text_IO; use Ada.Text_IO; with Mod_Attribute; procedure Main is type Signed_Integer is range 1..1_000_000; type Modular is mod 2**32; begin declare X : Modular := 1; begin X := - X; pragma Assert (X = 2**32-1); pragma Assert (X = 16#FFFF_FFFF#); Put_Line ("X =" & X'Img); -- prints "X = 4294967295" end; declare I : Integer := -1; X : Modular; begin X := Modular (I); -- raises Constraint_Error -- GNAT warns here: -- warning: value not in range of type "Modular" defined at line 7 -- warning: "Constraint_Error" will be raised at run time Put_Line ("X =" & X'Img); -- doesn't print anything exception when Constraint_Error => Put_Line ("Constraint_Error raised."); end; declare type My_Modular is mod 2**16; package M is new Mod_Attribute (Formal_Modular => My_Modular); begin pragma Assert (M.F = 2#1111_1111_1111_1111#); Put_Line("M.F =" & M.F'Img); -- prints "M.F = 65535". end; end Main;