What is a conformant array?

Conformant array is a feature of secure programming languages. Pascal and C appeared at nearly the same years. In C arrays were not different to pointers much, so passing array of unknown size in C is virtually passing a pointer to the beginning of array and a length argument.

Pascal was the first language to propose index checking. But procedures and functions can no longer process arrays of different sizes this way due to type mismatch. C's insecure way of array parameter handling was not compatible with secure programming language design. So conformant-array-parameter kind of parameter was introduced. It could only be a formal parameter type, cannot appear anywhere else.

ISO Pascal 83 is probably no longer in use as is, but the same approach can be seen in Pascal derivatives. Open arrays is a feature introduced in Delphi 4, 5 or so. Previously Turbo Pascal had a feature called open strings, but that is a feature for another demands, and more limited one. For instance, in open array the length is passed as an implicit parameter, and in open string the length is stored in the first byte of string, while implicit parameter is used to communicate the maximal possible length.

This feature enables secure array access. A function or procedure accepting an open array can be passed either fixed array of any size, or a dynamic array, or an open array (obtained from another caller), or a slice of any of them, or an open array constructor. Unfortunately, the Delphi engineers do not value secure programming that much, so Delphi is not marketed and not widely known as a secure programming language. Index and range checking is not enabled by default. Conformant arrays are not marketed as a kind of killer feature for fast and secure programming. System.Slice unlike System.Copy does not take the Index parameter, so it is only possible to reference sub-array starting from the beginning of array using intrinsic functions. There are no technical limitations to reference sub-array starting and ending in any part of greater array, and it's possible to workaround this limitation, but fixing the System.Slice would be a more sound solution. That should be simple enough, but for many years there seem to be nobody to work on Delphi's secure programming features.

ISO Pascal's conformant arrays is an optional feature. Ada is derived from Pascal, but in Ada there is a more powerful feature called unconstrained arrays, and this feature is a required one. Ada is probably the most interesting language to play with conformant arrays. It is a secure programming language that should be a default for systems programming, but unfortunately people are so little aware of it and invent ugly kludges where migrating to Ada would be enough. No need for iMPX, no need for SGX, Ada is enough.

In Ada, unconstrained arrays are particular case of indefinite types. A good sample of unconstrained array and an indefinite type is Standard.String, also known as fixed string. Instances of String can be of any length. Allocated instances are mutable, but constraints are fixed. This way procedure or function accepting a String argument would effectively accept string of any size. Note that in Ada unconstrained array can (and must) have a type name. Conformant arrays in ISO Pascal and open arrays in Delphi can only appear as function argument formal types, and thus cannot appear in type declaration, and thus cannot have a name.

Indefinite types in Ada are allowed virtually anywhere. They can be used as formal parameters, acting like conformant and open arrays. It is possible to pass a slice of array, starting from any index. Variables and constants can be of indefinite type, but they must be initialized to make constraints definite at runtime. Unlike Delphi, in Ada it is possible to initialize not only global variables and constants, but also local ones. Also, it is possible to use expressions to initialize items. Actually, in Delphi aggregates are only allowed in declared constants and open array constructors, while in Ada aggregates are particular case of expressions, no different to any other expression, usable in other contexts, and when variable or constant is being initialized with an aggregate, this is a particular case of initializing with expression. In Ada indefinite variables and constants are allocated on stack, similar to non-standard alloca() C extension and C99 variable-length arrays

Indefinite types are allowed in Ada as return types. It is not possible to preallocate space on primary stack for result of unknown size. When primary stack is already piled with arguments and local variables, it is not suitable for returning the result. Other programming languages regress to using heap in this case. Ada compilers (at least, GNAT and AdaMagic are known to) perform an optimization: they use a secondary stack. Allocation and deallocation is as fast as on a primary one, but there is no pile of arguments and local variables there. For instance, this way Ada.Text_IO.Get_Line function works.

Also, in contrast to Delphi, Ada has pointers (access types) to indefinite types. So far, indefinite types can appear virtually anywhere, and Ada compilers know how to orchestrate primary and secondary stacks to achieve the highest performance compared to other programming languages relying on heap more often. Of course, items inside records cannot have arbitrary sizes, all items inside records must be definite, however, constraints can be specified using type discriminants. As far as I can tell, Ada managed to make indefinite type values as much first-class citizens as possible in a native programming language. It is interesting to watch a process where the higher the push towards security, the more expressive features a programming language has to get to be able to express all possible tricks that can be proved to be secure.

Apart from Pascal derivatives, there are several non-Pascal languages that are worth to mention. Unfortunately, people behind these languages either prefer not to see Pascal and its derivatives to make their programming language shine in front of what remains. Or they are ignorant to the degree that is not acceptable for programming language engineers. The CPU design eventually leads to the same design solutions as in ISO Pascal, Delphi, Ada, but they present their “discoveries” as not related to Pascal or its derivatives.

For instance, ISO C++ is about to get conformant arrays. C++ Core Guidelines introducing GSL having so called "span" non-owning container. Procedures and functions can accept span template, and this way array or vector can be accepted. Unlike Ada, "span" is always non-owning. Only arrays and vectors are owning. Like I said, those people suffer the “not invented here” syndrome very much, so if you look at bibliography, you'll see “… C++ …”, “… C++ …”, “… C++ …”, “… C++ …”, … all the way to the bottom. They act like sect. If they are aware, then it means their actions are non-ethical, and somehow they convinced enough people to not ever reference anything related to Pascal, Delphi or Ada. Or maybe they are for real unacceptably unaware and for real reinventing Pascal. Such badly unaware people should do homework for years before being allowed to design programming languages, but somehow in our crazy world it happens.

Rust was positioned as a secure programming language. It was expected that there will be a comparison with another secure programming languages, Ada being the most visible one, used in airplanes and metro where lives matter. In academia ones have to make “Previous work” part of their research reports, so a programming language positioned this way is expected to have a comparison with Ada in a “Previous work” part, but Rust engineers either belong to the same sect as C++ engineers, and their religion forbids them referencing Pascal, Delphi or Ada; or they somehow accidentally happened to also be unacceptably unaware about Ada despite exploring the virtually the same area of research. Not mentioning Delphi and Ada makes them easier to promote their programming language Rust, because this way they can compare with C and C++ which are an easy target of insecurity critics, and Ada would be a way too tough competitor for such a young language. Well, seems like their sectarian plan works good enough, as there are much people looking for security going to Rust as opposed to Ada, not being informed about Ada somehow.

Anyway, in the end the same feature is present in Rust too. Types in Rust can have no size markers, and there are array slices as built-in type. Rust arrays and array slices are more powerful than in Delphi and are mostly close to Ada arrays. But unlike Ada, it is not possible in Rust to return unsized value. Rust functions have to explicitly use slow heap.

I can order reviewed programming languages according to their level of conformant arrays support this way: Ada, Rust, Delphi, C++. That should be comprehensive review about conformant arrays and related features situation in programming languages.


So this is a really old question, but it comes up early in search results, so let me give the correct answer.

A Conformant Array isn't a dynamic array that resizes itself to fit data, like Java's List class. It's a mechanism that lets Pascal procedures and functions work with variable-sized arrays.

In Pascal, the size of an array is part of its type, so in:

VAR
    MyArray1 : ARRAY [1..10] OF INTEGER;
    MyArray2 : ARRAY [1..20] OF INTEGER;

MyArray1 and MyArray2 are of two different types and aren't compatible.

This becomes a problem with procedures/functions in classic Pascal: because the formal parameters of a procedure must have a specific type, and because an array's length is part of its type, it is in general not possible to define a procedure/function that works on arrays of arbitrary lengths.

Conformant Arrays were introduced as part of ISO Standard Pascal to work around this issue by allowing a procedure/function to be defined taking a "conformant array" as a parameter, like this:

PROCEDURE MyProc(VAR x : ARRAY [low..high : INTEGER] OF INTEGER);

This defines procedure MyProc with three formal parameters: x, low, and high (i.e. there are three parameters being passed in of arbitrary name). When the procedure is invoked, x is set to a pointer to the array, low is an integer variable set to the lower bound of the array and high is an integer variable set to the upper bound of the array.

This mechanism allows array bounds to be passed with the array in a type-safe manner, and lets procedures work with arrays of arbitrary size. It's a feature defined by ISO Standard Pascal, but other Pascals (e.g. Extended Pascal and Borland Pascal) came up with different mechanisms to deal with this problem (e.g. Extended Pascal's type schemas, which are much richer and more complicated).


This is an even older question (now), but it came up as the first answer when I googled for "what is conformant array". On Windows, in terms of IDL, and the languages that use IDL (C & C++) the definition is here.

Quoting the article: "An array is called "conformant" if the upper bound of any dimension is determined at runtime. (Only upper bounds can be determined at runtime.) "

Tags:

Arrays

Pascal