3

I am working on a legacy Ada 95 project (inherited from some other company) which was originally compiled using GNAT 3.13a1. I have updated the compiler to GNAT 4.7.4, to get access to some GNAT libraries that I would like to use in further development, but I am using the -gnat95 flag.

I am having trouble getting a particular file to compile. It's a bit of an odd one, in that it was named "Ada.Calendar.GMT". This was a problem for the new compiler, which complained that I could not define a package which was a child package of a package in the Ada package tree. I figured this wasn't too big a problem, so I changed the name of the package to "GMT_Library", imported Ada.Calendar to get access to it's types, and changed the name of the file to suit (so as to prevent a further compiler warning).

But now the compiler complains that casting an Ada.Calendar.Time to a Duration is not valid. Specifically, I am given the following error:

180.       D := Duration (Date);
                         |
     >>> illegal operand for numeric conversion

Other than changing the name of the package, and importing Ada.Calendar, I have not changed this source file, or the corresponding spec file, in any other way. Why was this an allowed operation in GNAT 3.13a1? Why does it no longer work, and is there a solution?

EDIT:

D is a Duration, and Date is an Ada.Calendar.Time.

After further investigation, it appears that the original developers mostly copied the implementation of Ada.Calendar defined here. That file performs the same cast in the Split procedure. So why is it okay for the standard libraries to do it?

Ogre
  • 781
  • 3
  • 10
  • 30
  • I don't think we have enough information. What's `Date`? What's `Time`? In any event, I've run across a number of illegal constructs that GNAT has accepted over the year, and sometimes they fix the bug. – ajb Sep 23 '14 at 04:38
  • Edited with some extra information. – Ogre Sep 23 '14 at 05:39
  • OK, seeing that `Date` is `Ada.Calendar.Time` is the key here. @egilhh's answer gave you the answer: the body of `Ada.Calendar.GMT` would have access to private information in `Ada.Calendar`, while a non-child package would not. – ajb Sep 23 '14 at 15:43

3 Answers3

3

Ada.Calendar.Time is a private type, and only the private part or package body of Ada.Calendar or any children of Ada.Calendar (in your case Ada.Calendar.GMT) can see the full declaration of the type, and the full declaration of Time is needed for the type cast to work (be aware though that the range of Duration is not required to support the full range of Time, so you may have some issues there as well. This is highly implementation dependent).

In GNAT, you are by default forbidden to create or modify any packages in the runtime (Ada., System., GNAT.*), but compiling with -gnatg will enable this. (It is not recommended to compile other packages with -gnatg, as it will turn on a bunch of warnings, style checks, etc as well, which you may not need/want)

egilhh
  • 6,464
  • 1
  • 18
  • 19
  • Goodness yes, -gnatg is quite strict! I now understand why this file is so much better formatted than the rest of them, this flag was required. – Ogre Sep 26 '14 at 05:46
2

Ada.Calendar can convert an Ada.Calendar.Date to a Duration because it can see the private view, as could Ada.Calendar.GMT if you could get it to compile.

@egilhh is right that you can use -gnatg to enable this, and I wouldn’t necessarily be against it (though you may have issues compiling the parts of your application that with Ada.Calendar.GMT;).

As an alternative, you could use Ada.Unchecked_Conversion in your renamed GMT_Library.

Whichever way you go, be aware that the epoch for Ada.Calendar.Time changed between GNAT 95 and GNAT 05. In GNAT 95, the epoch was the Unix epoch (1970-01-01 00:00:00.00). The easiest way round this might be to use Ada.Calendar.Time_Of (Year => 1970, ...), converted to Duration, as the basis for your calculations.

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
0

When you think of it, a Time is a specific moment in time, whereas a duration expresses the elapsed time since a specific event. So it does not make sense semantically to convert from one to the other, that would be like converting from kilograms to meters (or pounds to inches for the US people).

Now, the GNAT runtime itself is not implemented in Ada, but in a slightly different version with different permissions and settings (that is why it must be compiled with -gnatg for instance). Of course, the differences are subtle.

When interfacing with a C library (with no-so-strong typing), it is often the case that we end up having to convert types in a fashion that does not make sense in a strongly typed language like Ada, so you should not do the same in your own packages. Perhaps using an Unchecked_Conversion would work, but I really do not recommend it.

Trying to guess the intent of your Ada.Calendar.GMT package just based on its name (always risky), it seems to me you should take a look at Ada05's Ada.Calendar.Time_Zones package or perhaps Ada.Calendar.Conversions. Technically, those are not Ada95, but I believe they are compatible.

manuBriot
  • 2,755
  • 13
  • 21
  • The intent seems to be that it allows retrieval of Year, Month, Day, and Seconds from an Ada.Calendar.Time, but in such a way that it represents a GMT time instead of the local time. From what I've been able to find, it copies Ada.Calendar's Split procedure, but calls gmtime_r instead of localtime_r. The last major work on this project was done in the year 2000, so the original developers didn't have access to Ada05 libraries at the time. – Ogre Sep 24 '14 at 00:06