tctc a 1 (cforall) user manual 2 version 1.0 3 “describe not prescribe” c a team andrew beach,...
TRANSCRIPT
DRA
FTC
A
(Cforall) User Manual1
Version 1.02
“describe not prescribe”3
C
A
TeamAndrew Beach, Richard Bilson, Peter A. Buhr, Thierry Delisle,
Glen Ditchfield, Rodolfo G. Esteves, Aaron Moss, Rob Schluntz
4
March 5, 20205
© 2016 C
A
Project6
7
This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of8
this license, visit http://creativecommons.org/licenses/by/4.0.9
Contents1
1 Introduction 12
1.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Why fix C? 24
3 History 35
4 Interoperability 36
5 Compiling a CFA Program 47
6 Backquote Identifiers 58
7 Constant Underscores 69
8 Exponentiation Operator 610
9 Control Structures 711
9.1 if/while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
9.2 case Clause . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
9.3 switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814
9.4 Non-terminating and Labelled fallthrough . . . . . . . . . . . . . . . . . . . . . . . . . . . 1215
9.5 Loop Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1216
9.6 Labelled continue / break Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317
10 with Statement 1318
11 Exception Handling 1819
11.1 Exception Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1820
12 Alternative Declarations 1921
13 Pointer / Reference 2022
13.1 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2323
13.2 Address-of Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2524
13.3 Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2625
14 Routine Definition 2626
14.1 Named Return Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2727
14.2 Routine Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2828
15 Routine Pointers 2929
16 Named and Default Arguments 2930
17 Unnamed Structure Fields 3231
ii
CONTENTS iii
18 Nesting 321
18.1 Type Nesting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
18.2 Routine Nesting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
19 Tuple 334
19.1 Multiple-Return-Value Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
19.2 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
19.3 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
19.4 Indexing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
19.5 Flattening and Structuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
19.6 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3710
19.7 Construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3911
19.8 Member-Access Expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3912
19.9 Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4013
19.10Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4114
19.10.1 Assertion Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4215
20 Tuples 4316
20.1 Tuple Coercions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4417
20.2 Mass Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4518
20.3 Multiple Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4619
20.4 Cascade Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4620
21 Stream I/O Library 4721
21.1 Implicit Separator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4822
21.2 Separation Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4823
21.3 Newline Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5024
21.4 Output Value Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5025
21.5 Input Value Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5326
22 Types 5427
22.1 Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5428
22.2 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5529
23 Constructors and Destructors 5630
24 Overloading 5631
24.1 Overloaded Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5632
24.2 Variable Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5833
24.3 Function Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5834
24.4 Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5935
25 Auto Type-Inferencing 6036
26 Concurrency 6137
26.1 Coroutine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6138
26.2 Monitors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6139
26.3 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6140
iv CONTENTS
27 Language Comparisons 611
27.1 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
27.2 Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
27.3 Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
27.4 D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
A Syntax Ambiguities 656
B C Incompatibles 667
C C
A
Keywords 698
D Standard Headers 709
E Standard Library 7010
E.1 Storage Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7011
E.2 String to Value Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7212
E.3 Search / Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7313
E.4 Absolute Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7314
E.5 Random Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7415
E.6 Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7416
F Math Library 7417
F.1 General . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7418
F.2 Exponential . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7519
F.3 Logarithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7520
F.4 Trigonometric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7621
F.5 Hyperbolic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7722
F.6 Error / Gamma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7823
F.7 Nearest Integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7924
F.8 Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8025
G Time Keeping 8026
G.1 Duration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8027
G.2 timeval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8228
G.3 timespec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8229
G.4 itimerval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8230
G.5 Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8231
H Clock 8332
H.1 C time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8333
H.2 Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8434
I Multi-precision Integers 8435
J Rational Numbers 8736
Index 9137
1 Introduction1
C
A1 is a modern general-purpose programming-language, designed as an evolutionary step forward for the2
C programming language. The syntax of C
A
builds from C and should look immediately familiar to C/C++3
programmers. C
A
adds many modern programming-language features that directly lead to increased safety4
and productivity, while maintaining interoperability with existing C programs and achieving similar perfor-5
mance. Like C, C
A
is a statically typed, procedural (non-object-oriented) language with a low-overhead6
runtime, meaning there is no global garbage-collection, but regional garbage-collection is possible. The7
primary new features include polymorphic routines and types, exceptions, concurrency, and modules.8
One of the main design philosophies of C
A
is to “describe not prescribe”, which means C
A
tries to9
provide a pathway from low-level C programming to high-level C
A
programming, but it does not force10
programmers to “do the right thing”. Programmers can cautiously add C
A
extensions to their C programs in11
any order and at any time to incrementally move towards safer, higher-level programming. A programmer is12
always free to reach back to C from C
A
, for any reason, and in many cases, new C
A
features can be locally13
switched back to there C counterpart. There is no notion or requirement for rewriting a legacy C program in14
C
A
; instead, a programmer evolves a legacy program into C
A
by incrementally incorporating C
A
features.15
As well, new programs can be written in C
A
using a combination of C and C
A
features.16
C++ [28] had a similar goal 30 years ago, allowing object-oriented programming to be incrementally17
added to C. However, C++ currently has the disadvantages of a strong object-oriented bias, multiple legacy18
design-choices that cannot be updated, and active divergence of the language model from C, requiring19
significant effort and training to incrementally add C++ to a C-based project. In contrast, C
A
has 30 years of20
hindsight and a clean starting point.21
Like C++, there may be both an old and new ways to achieve the same effect. For example, the following22
programs compare the C, C
A
, and C++ I/O mechanisms, where the programs output the same result.23
C C
A
C++
#include <stdio.h>
int main( void ) {int x = 0, y = 1, z = 2;
printf( "%d %d %d\n", x, y, z );
}
#include <fstream>
int main( void ) {int x = 0, y = 1, z = 2;
sout | x | y | z;
}
#include <iostream>using namespace std;
int main() {int x = 0, y = 1, z = 2;
cout<<x<<" "<<y<<" "<<z<<endl;
}
24
While the C
A
I/O looks similar to the C++ output style, there are important differences, such as automatic25
spacing between variables as in Python (see Section ??, p. ??).26
1.1 Background27
This document is a programmer reference-manual for the C
A
programming language. The manual covers the28
core features of the language and runtime-system, with simple examples illustrating syntax and semantics29
of features. The manual does not teach programming, i.e., how to combine the new constructs to build30
complex programs. The reader must have an intermediate knowledge of control flow, data structures, and31
concurrency issues to understand the ideas presented, as well as some experience programming in C/C++.32
Implementers should refer to the C
A
Programming Language Specification for details about the language33
syntax and semantics. Changes to the syntax and additional features are expected to be included in later34
revisions.35
1Pronounced “C-for-all”, and written C
A
, CFA, or Cforall.
1
2 2 Why fix C?
2 Why fix C?1
The C programming language is a foundational technology for modern computing with millions of lines of2
code implementing everything from hobby projects to commercial operating-systems. This installation base3
and the programmers producing it represent a massive software-engineering investment spanning decades4
and likely to continue for decades more. Even with all its problems, C continues to be popular because it5
allows writing software at virtually any level in a computer system without restriction. For system program-6
ming, where direct access to hardware, storage management, and real-time issues are a requirement, C is7
usually the only language of choice. The TIOBE index [30] for February 2020 ranks the top six most8
popular programming languages as Java 17.4%, C 16.8%, Python 9.3%, C++ 6.2%, C♯ 5.9%, Visual Basic9
5.9% = 61.5%, where the next 50 languages are less than 2% each, with a long tail. The top 4 rankings over10
the past 35 years are:11
2020 2015 2010 2005 2000 1995 1990 1985
Java 1 2 1 2 3 - - -
C 2 1 2 1 1 2 1 1
Python 3 7 6 6 22 21 - -
C++ 4 4 4 3 2 1 2 12
12
Hence, C is still an extremely important programming language, with double the usage of C++; in many13
cases, C++ is often used solely as a better C. Love it or hate it, C has been an important and influential part14
of computer science for 40 years and its appeal is not diminishing. Nevertheless, C has many problems and15
omissions that make it an unacceptable programming language for modern needs.16
As stated, the goal of the C
A
project is to engineer modern language-features into C in an evolutionary17
rather than revolutionary way. C++ [7, 14] is an example of a similar project; however, it largely extended18
the C language, and did not address most of C’s existing problems.2 Fortran [16], Ada [1], and Cobol [8] are19
examples of programming languages that took an evolutionary approach, where modern language-features20
(e.g., objects, concurrency) are added and problems fixed within the framework of the existing language.21
Java [20], Go [21], Rust [27] and D [3] are examples of the revolutionary approach for modernizing C/C++,22
resulting in a new language rather than an extension of the descendent. These languages have different23
syntax and semantics from C, do not interoperate directly with C, and are not systems languages because24
of restrictive memory-management or garbage collection. As a result, there is a significant learning curve25
to move to these languages, and C legacy-code must be rewritten. These costs can be prohibitive for many26
companies with a large software-base in C/C++, and a significant number of programmers require retraining27
in the new programming language.28
The result of this project is a language that is largely backwards compatible with C11 [6], but fixes many29
of the well known C problems while adding modern language-features. To achieve these goals required a30
significant engineering exercise, where we had to “think inside the existing C box”. Without these signifi-31
cant extension to C, it is unable to cope with the needs of modern programming problems and programmers;32
as a result, it will fade into disuse. Considering the large body of existing C code and programmers, there33
is significant impetus to ensure C is transformed into a modern programming language. While C11 made34
a few simple extensions to the language, nothing was added to address existing problems in the language35
or to augment the language with modern language-features. While some may argue that modern language-36
features may make C complex and inefficient, it is clear a language without modern capabilities is insuffi-37
cient for the advanced programming problems existing today.38
2Two important existing problems addressed were changing the type of character literals from int to char and enumerator from int
to the type of its enumerators.
3 History 3
3 History1
The C
A
project started with Dave Till’s K-W C [5, 29], which extended C with new declaration syntax,2
multiple return values from routines, and advanced assignment capabilities using the notion of tuples.3
(See [31] for similar work in C++.) The first C
A
implementation of these extensions was by Rodolfo4
Esteves [15].5
The signature feature of C
A
is overloadable parametric-polymorphic functions [9, 10, 13] with functions6
generalized using a forall clause (giving the language its name):7
forall( otype T ) T identity( T val ) { return val; }8
int forty two = identity( 42 ); // T is bound to int, forty two == 429
C
A
’s polymorphism was originally formalized by Glen Ditchfield [11], and first implemented by Richard10
Bilson [2]. However, at that time, there was little interesting in extending C, so work did not continue. As the11
saying goes, “What goes around, comes around.”, and there is now renewed interest in the C programming12
language because of legacy code-bases, so the C
A
project has been restarted.13
4 Interoperability14
C
A
is designed to integrate directly with existing C programs and libraries. The most important feature of15
interoperability is using the same calling conventions, so there is no complex interface or overhead to call16
existing C routines. This feature allows C
A
programmers to take advantage of the existing panoply of C17
libraries to access thousands of external software features. Language developers often state that adequate18
library support takes more work than designing and implementing the language itself. Fortunately, C
A
,19
like C++, starts with immediate access to all exiting C libraries, and in many cases, can easily wrap library20
routines with simpler and safer interfaces, at very low cost. Hence, C
A
begins by leveraging the large21
repository of C libraries, and than allows programmers to incrementally augment their C programs with22
modern backward-compatible features.23
However, it is necessary to differentiate between C and C
A
code because of name overloading, as for24
C++. For example, the C math-library provides the following routines for computing the absolute value of25
the basic types: abs, labs, llabs, fabs, fabsf, fabsl, cabsf, cabs, and cabsl. Whereas, C
A
wraps each of these26
routines into ones with the overloaded name abs:27
char abs( char );28
extern "C" { int abs( int ); } // use default C routine for int29
long int abs( long int );30
long long int abs( long long int );31
float abs( float );32
double abs( double );33
long double abs( long double );34
float Complex abs( float Complex );35
double Complex abs( double Complex );36
long double Complex abs( long double Complex );37
The problem is the name clash between the library routine abs and the C
A
names abs. Hence, names38
appearing in an extern "C" block have C linkage. Then overloading polymorphism uses a mechanism39
called name mangling to create unique names that are different from C names, which are not mangled.40
Hence, there is the same need, as in C++, to know if a name is a C or C
A
name, so it can be correctly formed.41
There is no way around this problem, other than C’s approach of creating unique names for each pairing of42
operation and types.43
This example strongly illustrates a core idea in C
A
: the power of a name. The name “abs” evokes44
the notion of absolute value, and many mathematical types provide the notion of absolute value. Hence,45
4 5 Compiling a CFA Program
knowing the name abs is sufficient to apply it to any type where it is applicable. The time savings and safety1
of using one name uniformly versus N unique names cannot be underestimated.2
5 Compiling a C
A
Program3
The command cfa is used to compile a C
A
program and is based on the GNU gcc command, e.g.:4
cfa [ gcc options ] [ C/C
A
source files ] [ assembler/loader files ]5
C
A
programs having the following gcc flags turned on:6
std=gnu11 The 2011 C standard plus GNU extensions.7
fgnu89 inline Use the traditional GNU semantics for inline routines in C11 mode, which allows inline8
routines in header files.9
The following new C
A
options are available:10
CFA Only the C preprocessor and the C
A
translator steps are performed and the transformed program is11
written to standard output, which makes it possible to examine the code generated by the C
A
translator.12
The generated code starts with the standard C
A
prelude.13
debug The program is linked with the debugging version of the runtime system. The debug version14
performs runtime checks to help during the debugging phase of a C
A
program, but can substantially15
slow program execution. The runtime checks should only be removed after the program is completely16
debugged. This option is the default.17
nodebug The program is linked with the non-debugging version of the runtime system, so the execution18
of the program is faster. However, no runtime checks or asserts are performed so errors usually result19
in abnormal program behaviour or termination.20
help Information about the set of C
A
compilation flags is printed.21
nohelp Information about the set of C
A
compilation flags is not printed. This option is the default.22
quiet The C
A
compilation message is not printed at the beginning of a compilation.23
noquiet The C
A
compilation message is printed at the beginning of a compilation. This option is the24
default.25
The following preprocessor variables are available:26
CFA MAJOR is available during preprocessing and its value is the major version number of C
A
.327
CFA MINOR is available during preprocessing and its value is the minor version number of C
A
.28
CFA PATCH is available during preprocessing and its value is the patch level number of C
A
.29
CFA , CFORALL , and cforall are always available during preprocessing and have no value.30
These preprocessor variables allow conditional compilation of programs that must work differently in these31
situations. For example, to toggle between C and C
A
extensions, use the following:32
#ifndef CFORALL33
#include <stdio.h> // C header file34
#else35
#include <fstream> // C
A
header file36
#endif37
3 The C preprocessor allows only integer values in a preprocessor variable so a value like “1.0.0.0 ” is not allowed. Hence, the need
to have three variables for the major, minor and patch version number.
6 Backquote Identifiers 5
which conditionally includes the correct header file, if the program is compiled using gcc or cfa.1
The C
A
translator has multiple steps. The following flags control how the tranlator works, the stages run,2
and printing within a stage. The majority of these flags are used by C
A
developers, but some are occasionally3
useful to programmers.4
h, help print help message5
l, libcfa generate libcfa.c6
L, linemarks generate line marks7
m, no main do not replace main8
N, no linemarks do not generate line marks9
n, no prelude do not read prelude10
p, prototypes generate prototypes for prelude functions11
P, print one of:12
altexpr alternatives for expressions13
ascodegen as codegen rather than AST14
ast AST after parsing15
astdecl AST after declaration validation pass16
asterr AST on error17
astexpr AST after expression analysis18
astgen AST after instantiate generics19
box before box step20
ctordtor after ctor/dtor are replaced21
codegen before code generation22
declstats code property statistics23
parse yacc (parsing) debug information24
pretty prettyprint for ascodegen flag25
resolver before resolver step26
rproto resolver-proto instance27
rsteps resolver steps28
symevt symbol table events29
tree parse tree30
tuple after tuple expansion31
prelude dir <directory> prelude directory for debug/nodebug32
S, statistics <option-list> enable profiling information:33
counters,heap,time,all,none34
t, tree build in tree35
6 Backquote Identifiers36
C
A
introduces several new keywords (see Section C, p. 69) that can clash with existing C variable-names37
in legacy code. Keyword clashes are accommodated by syntactic transformations using the C
A
backquote38
escape-mechanism:39
int ‘‘otype = 3; // make keyword an identifier40
double ‘‘forall = 3.5;41
Existing C programs with keyword clashes can be converted by enclosing keyword identifiers in back-42
quotes, and eventually the identifier name can be changed to a non-keyword name. Figure 1 shows how43
clashes in existing C header-files (see Section D, p. 70) can be handled using preprocessor interposition:44
#include next and I filename. Several common C header-files with keyword clashes are fixed in the stan-45
6 8 Exponentiation Operator
// include file uses the CFA keyword "with".
#if ! defined( with ) // nesting ?
#define with ‘‘with // make keyword an identifier#define CFA BFD H
#endif
#include next <bfdlink.h> // must have internal check for multiple expansion
#if defined( with ) && defined( CFA BFD H ) // reset only if set
#undef with
#undef CFA BFD H
#endif
Figure 1: Header-File Interposition
dard C
A
header-library, so there is a seamless programming-experience.1
7 Constant Underscores2
Numeric constants are extended to allow underscores, e.g.:3
2 147 483 648; // decimal constant4
56 ul; // decimal unsigned long constant5
0 377; // octal constant6
0x ff ff; // hexadecimal constant7
0x ef3d aa5c; // hexadecimal constant8
3.141 592 654; // floating constant9
10 e +1 00; // floating constant10
0x ff ff p 3; // hexadecimal floating11
0x 1.ffff ffff p 128 l; // hexadecimal floating long constant12
L "\x ff ee"; // wide character constant13
The rules for placement of underscores are:14
1. A sequence of underscores is disallowed, e.g., 12 34 is invalid.15
2. Underscores may only appear within a sequence of digits (regardless of the digit radix). In other16
words, an underscore cannot start or end a sequence of digits, e.g., 1, 1 and 1 are invalid (actually,17
the 1st and 3rd examples are identifier names).18
3. A numeric prefix may end with an underscore; a numeric infix may begin and/or end with an under-19
score; a numeric suffix may begin with an underscore. For example, the octal 0 or hexadecimal 0x20
prefix may end with an underscore 0 377 or 0x ff; the exponent infix E may start or end with an21
underscore 1.0 E10, 1.0E 10 or 1.0 E 10; the type suffixes U, L, etc. may start with an underscore22
1 U, 1 ll or 1.0E10 f.23
It is significantly easier to read and enter long constants when they are broken up into smaller groupings24
(many cultures use comma and/or period among digits for the same purpose). This extension is backwards25
compatible, matches with the use of underscore in variable names, and appears in Ada and Java 8.26
8 Exponentiation Operator27
C, C++, and Java (and many other programming languages) have no exponentiation operator, i.e., xy, and28
instead use a routine, like pow(x,y), to perform the exponentiation operation. C
A
extends the basic operators29
with the exponentiation operator ?\? and ?\=?, as in, x \ y and x \= y, which means xy and x← xy. The priority30
9 Control Structures 7
of the exponentiation operator is between the cast and multiplicative operators, so that w * (int)x \ (int)y * z1
is parenthesized as ((w * (((int)x) \ ((int)y))) * z).2
There are exponentiation operators for integral and floating types, including the builtin complex types.3
Integral exponentiation is performed with repeated multiplication4 (or shifting if the exponent is 2). Over-4
flow for a large exponent or negative exponent returns zero. Floating exponentiation is performed using5
logarithms, so the exponent cannot be negative.6
sout | 1 \ 0 | 1 \ 1 | 2 \ 8 | 4 \ 3 | 5 \ 3 | 5 \ 32 | 5L \ 32 | 5L \ 64 | 4 \ 3 | 4.0 \ 3 | 4.0 \ 2.17
| (1.0f+2.0fi) \ (3.0f+2.0fi);8
1 1 256 64 125 0 3273344365508751233 0 0 0.015625 18.3791736799526 0.264715 1.1922i9
Note, 5 \ 32 and 5L \ 64 overflow, and 4 \ 3 is a fraction but stored in an integer so all three computations10
generate an integral zero. Parenthesis are necessary for complex constants or the expression is parsed as11
1.0f+(2.0fi \ 3.0f)+2.0fi. The exponentiation operator is available for all the basic types, but for user-defined12
types, only the integral-computation version is available.13
forall( otype OT | { void ?{}( OT & this, one t ); OT ?*?( OT, OT ); } )14
OT ?\?( OT ep, unsigned int y );15
forall( otype OT | { void ?{}( OT & this, one t ); OT ?*?( OT, OT ); } )16
OT ?\?( OT ep, unsigned long int y );17
The user type T must define multiplication, one (1), and *.18
9 Control Structures19
C
A
identifies inconsistent, problematic, and missing control structures in C, and extends, modifies, and adds20
control structures to increase functionality and safety.21
9.1 if/while Statement22
The if/while expression allows declarations, similar to for declaration expression. (Does not make sense for23
do-while.)24
if ( int x = f() ) ... // x != 025
if ( int x = f(), y = g() ) ... // x != 0 && y != 026
if ( int x = f(), y = g(); x < y ) ... // relational expression27
if ( struct S { int i; } x = { f() }; x.i < 4 ) // relational expression28
29
while ( int x = f() ) ... // x != 030
while ( int x = f(), y = g() ) ... // x != 0 && y != 031
while ( int x = f(), y = g(); x < y ) ... // relational expression32
while ( struct S { int i; } x = { f() }; x.i < 4 ) ... // relational expression33
Unless a relational expression is specified, each variable is compared not equal to 0, which is the standard34
semantics for the if/while expression, and the results are combined using the logical && operator.5 The scope35
of the declaration(s) is local to the @if@ statement but exist within both the “then” and “else” clauses.36
9.2 case Clause37
C restricts the case clause of a switch statement to a single value. For multiple case clauses associated with38
the same statement, it is necessary to have multiple case clauses rather than multiple values. Requiring a39
4The multiplication computation is O(logy).5C++ only provides a single declaration always compared not equal to 0.
8 9.3 switch Statement
case clause for each value does not seem to be in the spirit of brevity normally associated with C. Therefore,1
the case clause is extended with a list of values, as in:2
C
A
C
switch ( i ) {case 1, 3, 5:
...case 2, 4, 6:
...
}
switch ( i ) {case 1: case 3 : case 5:
...case 2: case 4 : case 6:
...
}
// odd values
// even values
3
In addition, subranges are allowed to specify case values.64
switch ( i ) {5
case 1∼5: // 1, 2, 3, 4, 56
...7
case 10∼15: // 10, 11, 12, 13, 14, 158
...9
}10
Lists of subranges are also allowed.11
case 1∼5, 12∼21, 35∼42:12
9.3 switch Statement13
C allows a number of questionable forms for the switch statement:14
1. By default, the end of a case clause7 falls through to the next case clause in the switch statement; to15
exit a switch statement from a case clause requires explicitly terminating the clause with a transfer16
statement, most commonly break:17
switch ( i ) {18
case 1:19
...20
// fall through21
case 2:22
...23
break; // exit switch statement24
}25
The ability to fall-through to the next clause is a useful form of control flow, specifically when a26
sequence of case actions compound:27
6 gcc has the same mechanism but awkward syntax, 2 ...42, because a space is required after a number, otherwise the period is a
decimal point.7 In this section, the term case clause refers to either a case or default clause.
9.3 switch Statement 9
switch ( argc ) {case 3:
// open output file// fall through
case 2:
// open input filebreak; // exit switch statement
default:
// usage message}
if ( argc == 3 ) {
// open output file// open input file
} else if ( argc == 2 ) {
// open input file (duplicate)
} else {
// usage message}
1
In this example, case 2 is always done if case 3 is done. This control flow is difficult to simulate with2
if statements or a switch statement without fall-through as code must be duplicated or placed in a3
separate routine. C also uses fall-through to handle multiple case-values resulting in the same action:4
switch ( i ) {5
case 1: case 3: case 5: // odd values6
// odd action7
break;8
case 2: case 4: case 6: // even values9
// even action10
break;11
}12
However, this situation is handled in other languages without fall-through by allowing a list of case13
values. While fall-through itself is not a problem, the problem occurs when fall-through is the default,14
as this semantics is unintuitive to many programmers and is different from virtually all other program-15
ming languages with a switch statement. Hence, default fall-through semantics results in a large16
number of programming errors as programmers often forget the break statement at the end of a case17
clause, resulting in inadvertent fall-through.18
2. It is possible to place case clauses on statements nested within the body of the switch statement:19
switch ( i ) {20
case 0:21
if ( j < k ) {22
...23
case 1: // transfer into "if" statement24
...25
} // if26
case 2:27
while ( j < 5 ) {28
...29
case 3: // transfer into "while" statement30
...31
} // while32
} // switch33
The problem with this usage is branching into control structures, which is known to cause both34
comprehension and technical difficulties. The comprehension problem occurs from the inability to35
determine how control reaches a particular point due to the number of branches leading to it. The36
technical problem results from the inability to ensure declaration and initialization of variables when37
blocks are not entered at the beginning. There are no positive arguments for this kind of control flow,38
and therefore, there is a strong impetus to eliminate it. Nevertheless, C does have an idiom where this39
10 9.3 switch Statement
capability is used, known as “Duff’s device” [12]:1
register int n = (count + 7) / 8;2
switch ( count % 8 ) {3
case 0: do{ *to = *from++;4
case 7: *to = *from++;5
case 6: *to = *from++;6
case 5: *to = *from++;7
case 4: *to = *from++;8
case 3: *to = *from++;9
case 2: *to = *from++;10
case 1: *to = *from++;11
} while ( n > 0 );12
}13
which unrolls a loop N times (N = 8 above) and uses the switch statement to deal with any iterations14
not a multiple of N. While efficient, this sort of special purpose usage is questionable:15
Disgusting, no? But it compiles and runs just fine. I feel a combination of pride and16
revulsion at this discovery. [12]17
3. It is possible to place the default clause anywhere in the list of labelled clauses for a switch statement,18
rather than only at the end. Virtually all programming languages with a switch statement require the19
default clause to appear last in the case-clause list. The logic for this semantics is that after checking20
all the case clauses without success, the default clause is selected; hence, physically placing the21
default clause at the end of the case clause list matches with this semantics. This physical placement22
can be compared to the physical placement of an else clause at the end of a series of connected if/else23
statements.24
4. It is possible to place unreachable code at the start of a switch statement, as in:25
switch ( x ) {26
int y = 1; // unreachable initialization27
x = 7; // unreachable code without label/branch28
case 0: ...29
...30
int z = 0; // unreachable initialization, cannot appear after case31
z = 2;32
case 1:33
x = z; // without fall through, z is uninitialized34
}35
While the declaration of the local variable y is useful with a scope across all case clauses, the initial-36
ization for such a variable is defined to never be executed because control always transfers over it.37
Furthermore, any statements before the first case clause can only be executed if labelled and trans-38
ferred to using a goto, either from outside or inside of the switch, both of which are problematic. As39
well, the declaration of z cannot occur after the case because a label can only be attached to a state-40
ment, and without a fall through to case 3, z is uninitialized. The key observation is that the switch41
statement branches into control structure, i.e., there are multiple entry points into its statement body.42
Before discussing potential language changes to deal with these problems, it is worth observing that in43
a typical C program:44
• the number of switch statements is small,45
9.3 switch Statement 11
• most switch statements are well formed (i.e., no Duff’s device),1
• the default clause is usually written as the last case-clause,2
• and there is only a medium amount of fall-through from one case clause to the next, and most of these3
result from a list of case values executing common code, rather than a sequence of case actions that4
compound.5
These observations put into perspective the C
A
changes to the switch.6
1. Eliminating default fall-through has the greatest potential for affecting existing code. However, even7
if fall-through is removed, most switch statements would continue to work because of the explicit8
transfers already present at the end of each case clause, the common placement of the default clause9
at the end of the case list, and the most common use of fall-through, i.e., a list of case clauses10
executing common code, e.g.:11
case 1: case 2: case 3: ...12
still works. Nevertheless, reversing the default action would have a non-trivial effect on case actions13
that compound, such as the above example of processing shell arguments. Therefore, to preserve14
backwards compatibility, it is necessary to introduce a new kind of switch statement, called choose,15
with no implicit fall-through semantics and an explicit fall-through if the last statement of a case-16
clause ends with the new keyword fallthrough/fallthru, e.g.:17
choose ( i ) {18
case 1: case 2: case 3:19
...20
// implicit end of switch (break)21
case 5:22
...23
fallthru; // explicit fall through24
case 7:25
...26
break // explicit end of switch (redundant)27
default:28
j = 3;29
}30
Like the switch statement, the choose statement retains the fall-through semantics for a list of case31
clauses; An implicit break is applied only at the end of the statements following a case clause. An32
explicit fallthru is retained because it is a C-idiom most C programmers expect, and its absence might33
discourage programmers from using the choose statement. As well, allowing an explicit break from34
the choose is a carry over from the switch statement, and expected by C programmers.35
2. Duff’s device is eliminated from both switch and choose statements, and only invalidates a small36
amount of very questionable code. Hence, the case clause must appear at the same nesting level as37
the switch/choose body, as is done in most other programming languages with switch statements.38
3. The issue of default at locations other than at the end of the cause clause can be solved by using good39
programming style, and there are a few reasonable situations involving fall-through where the default40
clause needs to appear is locations other than at the end. Therefore, no change is made for this issue.41
4. Dealing with unreachable code in a switch/choose body is solved by restricting declarations and42
associated initialization to the start of statement body, which is executed before the transfer to the43
12 9.5 Loop Control
appropriate case clause8 and precluding statements before the first case clause. Further declarations1
at the same nesting level as the statement body are disallowed to ensure every transfer into the body2
is sound.3
switch ( x ) {4
int i = 0; // allowed only at start5
case 0:6
...7
int j = 0; // disallowed8
case 1:9
{10
int k = 0; // allowed at different nesting levels11
...12
case 2: // disallow case in nested statements13
}14
...15
}16
9.4 Non-terminating and Labelled fallthrough17
The fallthrough clause may be non-terminating within a case clause or have a target label to common code18
from multiple case clauses.19
choose ( ... ) {case 3:
if ( ... ) {
... fallthru; // goto case 4} else {
...}
// implicit break
case 4:
choose ( ... ) {case 3:
... fallthrough common;
case 4:... fallthrough common;
common: // below fallthrough
// at case clause level
... // common code for cases 3/4// implicit break
case 4:
choose ( ... ) {case 3:
choose ( ... ) {
case 4:for ( ... ) {
// multi level transfer... fallthru common;
}
...}
...
common: // below fallthrough// at case clause level
20
The target label must be below the fallthrough and may not be nested in a control structure, and the target21
label must be at the same or higher level as the containing case clause and located at the same level as22
a case clause; the target label may be case default, but only associated with the current switch/choose23
statement.24
9.5 Loop Control25
The for/while/do while loop-control allows empty or simplified ranges (see Figure 2).26
• The loop index is polymorphic in the type of the comparison value N (when the start value is implicit)27
or the start value M.28
• An empty conditional implies comparison value of 1 (true).29
• A comparison N is implicit up-to exclusive range [0,N).30
8 Essentially, these declarations are hoisted before the switch/choose statement and both declarations and statement are surrounded
by a compound statement.
9.6 Labelled continue / break Statement 13
• A comparison = N is implicit up-to inclusive range [0,N].1
• The up-to range M ∼ N means exclusive range [M,N).2
• The up-to range M ∼= N means inclusive range [M,N].3
• The down-to range M ∼ N means exclusive range [N,M).4
• The down-to range M ∼= N means inclusive range [N,M].5
• 0 is the implicit start value;6
• 1 is the implicit increment value.7
• The up-to range uses operator += for increment;8
• The down-to range uses operator = for decrement.9
• @ means put nothing in this field.10
• : means start another index.11
9.6 Labelled continue / break Statement12
While C provides continue and break statements for altering control flow, both are restricted to one level13
of nesting for a particular control structure. Unfortunately, this restriction forces programmers to use goto14
to achieve the equivalent control-flow for more than one level of nesting. To prevent having to switch to the15
goto, C
A
extends the continue and break with a target label to support static multi-level exit [4], as in Java.16
For both continue and break, the target label must be directly associated with a for, while or do statement;17
for break, the target label can also be associated with a switch, if or compound ({}) statement. Figure 3,18
p. 15 shows continue and break indicating the specific control structure, and the corresponding C program19
using only goto and labels. The innermost loop has 7 exit points, which cause continuation or termination20
of one or more of the 7 nested control-structures.21
Both labelled continue and break are a goto restricted in the following ways:22
• They cannot create a loop, which means only the looping constructs cause looping. This restriction23
means all situations resulting in repeated execution are clearly delineated.24
• They cannot branch into a control structure. This restriction prevents missing declarations and/or initial-25
izations at the start of a control structure resulting in undefined behaviour.26
The advantage of the labelled continue/break is allowing static multi-level exits without having to use the27
goto statement, and tying control flow to the target control structure rather than an arbitrary point in a28
program. Furthermore, the location of the label at the beginning of the target control structure informs the29
reader (eye candy) that complex control-flow is occurring in the body of the control structure. With goto,30
the label is at the end of the control structure, which fails to convey this important clue early enough to the31
reader. Finally, using an explicit target for the transfer instead of an implicit target allows new constructs32
to be added or removed without affecting existing constructs. Otherwise, the implicit targets of the current33
continue and break, i.e., the closest enclosing loop or switch, change as certain constructs are added or34
removed.35
10 with Statement36
Grouping heterogeneous data into aggregates (structure/union) is a common programming practice, and an37
aggregate can be further organized into more complex structures, such as arrays and containers:38
struct S { // aggregate39
char c; // fields40
int i;41
double d;42
14 10 with Statement
loop control output
while () { sout | "empty"; break; }
do { sout | "empty"; break; } while ();for () { sout | "empty"; break; }
for ( 0 ) { sout | "A"; } sout | "zero";
for ( 1 ) { sout | "A"; }for ( 10 ) { sout | "A"; }
for ( = 10 ) { sout | "A"; }
for ( 1 ∼= 10 ∼ 2 ) { sout | "B"; }for ( 10 ∼= 1 ∼ 2 ) { sout | "C"; }
for ( 0.5 ∼ 5.5 ) { sout | "D"; }for ( 5.5 ∼ 0.5 ) { sout | "E"; }
for ( i; 10 ) { sout | i; }
for ( i; = 10 ) { sout | i; }for ( i; 1 ∼= 10 ∼ 2 ) { sout | i; }
for ( i; 10 ∼= 1 ∼ 2 ) { sout | i; }for ( i; 0.5 ∼ 5.5 ) { sout | i; }
for ( i; 5.5 ∼ 0.5 ) { sout | i; }
for ( ui; 2u ∼= 10u ∼ 2u ) { sout | ui; }for ( ui; 10u ∼= 2u ∼ 2u ) { sout | ui; }
enum { N = 10 };
for ( N ) { sout | "N"; }for ( i; N ) { sout | i; }
for ( i; N ∼ 0 ) { sout | i; }const int start = 3, comp = 10, inc = 2;
for ( i; start ∼ comp ∼ inc + 1 ) { sout | i; }
for ( i; 1 ∼ @ ) { if ( i > 10 ) break; sout | i; }for ( i; 10 ∼ @ ) { if ( i < 0 ) break; sout | i; }
for ( i; 2 ∼ @ ∼ 2 ) { if ( i > 10 ) break; sout | i; }for ( i; 2.1 ∼ @ ∼ @ ) { if ( i > 10.5 ) break; sout | i; i += 1.7; }
for ( i; 10 ∼ @ ∼ 2 ) { if ( i < 0 ) break; sout | i; }
for ( i; 12.1 ∼ @ ∼ @ ) { if ( i < 2.5 ) break; sout | i; i = 1.7; }for ( i; 5 : j; 5 ∼ @ ) { sout | i | j; }
for ( i; 5 : j; 5 ∼ @ ) { sout | i | j; }
for ( i; 5 : j; 5 ∼ @ ∼ 2 ) { sout | i | j; }for ( i; 5 : j; 5 ∼ @ ∼ 2 ) { sout | i | j; }
for ( i; 5 : j; 5 ∼ @ ) { sout | i | j; }for ( i; 5 : j; 5 ∼ @ ) { sout | i | j; }
for ( i; 5 : j; 5 ∼ @ ∼ 2 ) { sout | i | j; }
for ( i; 5 : j; 5 ∼ @ ∼ 2 ) { sout | i | j; }for ( i; 5 : j; 5 ∼ @ ∼ 2 : k; 1.5 ∼ @ ) { sout | i | j | k; }
for ( i; 5 : j; 5 ∼ @ ∼ 2 : k; 1.5 ∼ @ ) { sout | i | j | k; }
for ( i; 5 : k; 1.5 ∼ @ : j; 5 ∼ @ ∼ 2 ) { sout | i | j | k; }
empty
emptyempty
zero
AA A A A A A A A A A
A A A A A A A A A A A
B B B B BC C C C C
D D D D DE E E E E
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 101 3 5 7 9
10 8 6 4 20.5 1.5 2.5 3.5 4.5
5.5 4.5 3.5 2.5 1.5
2 4 6 8 1010 8 6 4 2
N N N N N N N N N N0 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1
3 6 9
1 2 3 4 5 6 7 8 9 1010 9 8 7 6 5 4 3 2 1 0
2 4 6 8 102.1 3.8 5.5 7.2 8.9
10 8 6 4 2 0
12.1 10.4 8.7 7. 5.3 3.60 5 1 4 2 3 3 2 4 1
0 5 1 6 2 7 3 8 4 9
0 5 1 3 2 1 3 1 4 30 5 1 7 2 9 3 11 4 13
0 5 1 4 2 3 3 2 4 10 5 1 6 2 7 3 8 4 9
0 5 1 3 2 1 3 1 4 3
0 5 1 7 2 9 3 11 4 130 5 1.5 1 7 2.5 2 9 3.5 3 11 4.5 4 13 5.5
0 5 1.5 1 7 2.5 2 9 3.5 3 11 4.5 4 13 5.5
0 5 1.5 1 7 2.5 2 9 3.5 3 11 4.5 4 13 5.5
Figure 2: Loop Control Examples
10 with Statement 15
C
A
C
LC: {... declarations ...
LS: switch ( ... ) {case 3:
LIF: if ( ... ) {
LF: for ( ... ) {LW: while ( ... ) {
... break LC; ...
... break LS; ...
... break LIF; ...
... continue LF; ...
... break LF; ...
... continue LW; ...
... break LW; ...} // while
} // for} else {
... break LIF; ...
} // if} // switch
} // compound
{... declarations ...
switch ( ... ) {case 3:
if ( ... ) {
for ( ... ) {while ( ... ) {
... goto LC; ...
... goto LS; ...
... goto LIF; ...
... goto LFC; ...
... goto LFB; ...
... goto LWC; ...
... goto LWB; ...LWC: ; } LWB: ;
LFC: ; } LFB: ;} else {
... goto LIF; ...
} L3: ;} LS: ;
} LC: ;
// terminate compound// terminate switch
// terminate if
// continue loop// terminate loop
// continue loop
// terminate loop
// terminate if
Figure 3: Multi-level Exit
};1
S s, as[10];2
However, functions manipulating aggregates must repeat the aggregate name to access its containing fields:3
void f( S s ) {4
s.c; s.i; s.d; // access containing fields5
}6
which extends to multiple levels of qualification for nested aggregates. A similar situation occurs in object-7
oriented programming, e.g., C++:8
struct S {9
char c; // fields10
int i;11
double d;12
void f() { // implicit “this” aggregate13
this >c; this >i; this >d; // access containing fields14
}15
}16
Object-oriented nesting of member functions in a class/struct allows eliding this > because of lexical17
scoping. However, for other aggregate parameters, qualification is necessary:18
struct T { double m, n; };19
int S::f( T & t ) { // multiple aggregate parameters20
c; i; d; // this–>.c, this–>.i, this–>.d21
t.m; t.n; // must qualify22
}23
To simplify the programmer experience, C
A
provides a with statement (see Pascal [23, § 4.F]) to elide24
16 10 with Statement
aggregate qualification to fields by opening a scope containing the field identifiers. Hence, the qualified1
fields become variables with the side-effect that it is easier to optimizing field references in a block.2
void f( S & this ) with ( this ) { // with statement3
c; i; d; // this.c, this.i, this.d4
}5
with the generality of opening multiple aggregate-parameters:6
void f( S & s, T & t ) with ( s, t ) { // multiple aggregate parameters7
c; i; d; // s.c, s.i, s.d8
m; n; // t.m, t.n9
}10
In detail, the with statement has the form:11
with-statement :12
'with' '(' expression-list ')' compound-statement13
and may appear as the body of a function or nested within a function body. Each expression in the14
expression-list provides a type and object. The type must be an aggregate type. (Enumerations are already15
opened.) The object is the implicit qualifier for the open structure-fields.16
All expressions in the expression list are open in parallel within the compound statement. This semantic17
is different from Pascal, which nests the openings from left to right. The difference between parallel and18
nesting occurs for fields with the same name and type:19
struct S { int i; int j; double m; } s, w;20
struct T { int i; int k; int m; } t, w;21
with ( s, t ) {22
j + k; // unambiguous, s.j + t.k23
m = 5.0; // unambiguous, t.m = 5.024
m = 1; // unambiguous, s.m = 125
int a = m; // unambiguous, a = s.i26
double b = m; // unambiguous, b = t.m27
int c = s.i + t.i; // unambiguous, qualification28
(double)m; // unambiguous, cast29
}30
For parallel semantics, both s.i and t.i are visible, so i is ambiguous without qualification; for nested seman-31
tics, t.i hides s.i, so i implies t.i. C
A
’s ability to overload variables means fields with the same name32
but different types are automatically disambiguated, eliminating most qualification when opening multiple33
aggregates. Qualification or a cast is used to disambiguate.34
There is an interesting problem between parameters and the function-body with, e.g.:35
void ?{}( S & s, int i ) with ( s ) { // constructor36
s.i = i; j = 3; m = 5.5; // initialize fields37
}38
Here, the assignment s.i = i means s.i = s.i, which is meaningless, and there is no mechanism to qualify39
the parameter i, making the assignment impossible using the function-body with. To solve this problem,40
parameters are treated like an initialized aggregate:41
struct Params {42
S & s;43
int i;44
} params;45
and implicitly opened after a function-body open, to give them higher priority:46
void ?{}( S & s, int i ) with ( s ) with( params ) {47
10 with Statement 17
s.i = i; j = 3; m = 5.5;1
}2
Finally, a cast may be used to disambiguate among overload variables in a with expression:3
with ( w ) { ... } // ambiguous, same name and no context4
with ( (S)w ) { ... } // unambiguous, cast5
and with expressions may be complex expressions with type reference (see Section ??) to aggregate:6
In object-oriented programming, there is an implicit first parameter, often names self or this, which is7
elided.8
class C {9
int i, j;10
int mem() { // implicit "this" parameter11
i = 1; // this->i12
j = 2; // this->j13
}14
}15
Since C
A
is non-object-oriented, the equivalent object-oriented program looks like:16
struct S { int i, j; };17
int mem( S & this ) { // explicit "this" parameter18
this.i = 1; // "this" is not elided19
this.j = 2;20
}21
but it is cumbersome having to write “this.” many times in a member.22
C
A
provides a with clause/statement (see Pascal [23, § 4.F]) to elided the "this." by opening a scope23
containing field identifiers, changing the qualified fields into variables and giving an opportunity for opti-24
mizing qualified references.25
int mem( S & this ) with( this ) { // with clause26
i = 1; // this.i27
j = 2; // this.j28
}29
which extends to multiple routine parameters:30
struct T { double m, n; };31
int mem2( S & this1, T & this2 ) with( this1, this2 ) {32
i = 1; j = 2;33
m = 1.0; n = 2.0;34
}35
The statement form is used within a block:36
int foo() {37
struct S1 { ... } s1;38
struct S2 { ... } s2;39
with( s1 ) { // with statement40
// access fields of s1 without qualification41
with s2 { // nesting42
// access fields of s1 and s2 without qualification43
}44
}45
with s1, s2 {46
// access unambiguous fields of s1 and s2 without qualification47
}48
18 11.1 Exception Hierarchy
}1
When opening multiple structures, fields with the same name and type are ambiguous and must be fully2
qualified. For fields with the same name but different type, context/cast can be used to disambiguate.3
struct S { int i; int j; double m; } a, c;4
struct T { int i; int k; int m } b, c;5
with( a, b )6
{7
}8
11 Exception Handling9
Exception handling provides two mechanism: change of control flow from a raise to a handler, and commu-10
nication from the raise to the handler. Transfer of control can be local, within a routine, or non-local, among11
routines. Non-local transfer can cause stack unwinding, i.e., non-local routine termination, depending on12
the kind of raise.13
exception t E {}; // exception type14
void f(...) {15
... throw E{}; ... // termination16
... throwResume E{}; ... // resumption17
}18
try {19
f(...);20
} catch( E e ; boolean-predicate ) { // termination handler21
// recover and continue22
} catchResume( E e ; boolean-predicate ) { // resumption handler23
// repair and return24
} finally {25
// always executed26
}27
The kind of raise and handler match: throw with catch and throwResume with catchResume. Then the28
exception type must match along with any additonal predicate must be true. The catch and catchResume29
handlers may appear in any oder. However, the finally clause must appear at the end of the try statement.30
11.1 Exception Hierarchy31
An exception type can be derived from another exception type, just like deriving a subclass from a class,32
providing a kind of polymorphism among exception types. The exception-type hierarchy that is created is33
used to organize exception types, similar to a class hierarchy in object-oriented languages, e.g.:34
IO Arithmetic
File Network DivideByZero Overflow Underflow
Exception
35
A programmer can then choose to handle an exception at different degrees of specificity along the hierarchy;36
derived exception-types support a more flexible programming style. For example, higher-level code should37
catch general exceptions to reduce coupling to the specific implementation at the lower levels; unnecessary38
coupling may force changes in higher-level code when low-level code changes. A consequence of derived39
exception-types is that multiple exceptions may match, e.g.:40
12 Alternative Declarations 19
catch( Arithmetic )1
matches all three derived exception-types: DivideByZero, Overflow, and Underflow. Because the propagation2
mechanisms perform a simple linear search of the handler clause for a guarded block, and selects the first3
matching handler, the order of catch clauses in the handler clause becomes important, e.g.:4
try {5
...6
} catch( Overflow ) { // must appear first7
// handle overflow8
} catch( Arithmetic )9
// handle other arithmetic issues10
}11
Multiple derivation among exception is not supported.12
12 Alternative Declarations13
C declaration syntax is notoriously confusing and error prone. For example, many C programmers are14
confused by a declaration as simple as:15
int * x[5] 0 1 2 3 4
0 1 2 3 4
x x16
Is this an array of 5 pointers to integers or a pointer to an array of 5 integers? If there is any doubt, it implies17
productivity and safety issues even for basic programs. Another example of confusion results from the fact18
that a routine name and its parameters are embedded within the return type, mimicking the way the return19
value is used at the routine’s call site. For example, a routine returning a pointer to an array of integers is20
defined and used in the following way:21
int (*f())[5] {...}; // definition22
... (*f())[3] += 1; // usage23
Essentially, the return type is wrapped around the routine name in successive layers (like an onion). While24
attempting to make the two contexts consistent is a laudable goal, it has not worked out in practice, even25
though Dennis Richie believed otherwise:26
In spite of its difficulties, I believe that the C’s approach to declarations remains plausible, and27
am comfortable with it; it is a useful unifying principle. [26, p. 12]28
C
A
provides its own type, variable and routine declarations, using a different syntax. The new declara-29
tions place qualifiers to the left of the base type, while C declarations place qualifiers to the right of the base30
type. In the following example, red is the base type and blue is qualifiers. The C
A
declarations move the31
qualifiers to the left of the base type, i.e., move the blue to the left of the red, while the qualifiers have the32
same meaning but are ordered left to right to specify a variable’s type.33
C
A
C
[5] * int x1;
* [5] int x2;
[* [5] int] f( int p );
int * x1 [5];int (*x2)[5];
int (*f( int p ))[5];
34
The only exception is bit field specification, which always appear to the right of the base type. However,35
unlike C, C
A
type declaration tokens are distributed across all variables in the declaration list. For instance,36
variables x and y of type pointer to integer are defined in C
A
as follows:37
C
A
C
* int x, y; int *x, *y;38
20 13 Pointer / Reference
The downside of this semantics is the need to separate regular and pointer declarations:1
C
A
C
* int x;int y;
int *x, y;2
which is prescribing a safety benefit. Other examples are:3
C
A
C
[ 5 ] int z;[ 5 ] * char w;
* [ 5 ] double v;
struct s {int f0:3;
* int f1;[ 5 ] * int f2;
};
int z[ 5 ];char * w[ 5 ];
double (* v)[ 5 ];
struct s {int f0:3;
int * f1;int * f2[ 5 ]
};
// array of 5 integers// array of 5 pointers to char
// pointer to array of 5 doubles
// common bit field syntax
4
All type qualifiers, e.g., const, volatile, etc., are used in the normal way with the new declarations and5
also appear left to right, e.g.:6
C
A
C
const * const int x;
const * [ 5 ] const int y;
int const * const x;
const int (* const y)[ 5 ]
// const pointer to const integer
// const pointer to array of 5 const integers
7
All declaration qualifiers, e.g., extern, static, etc., are used in the normal way with the new declarations but8
can only appear at the start of a C
A
routine declaration,9 e.g.:9
C
A
C
extern [ 5 ] int x;
static * const int y;
int extern x[ 5 ];
const int static * y;
// externally visible array of 5 integers
// internally visible pointer to constant int
10
The new declaration syntax can be used in other contexts where types are required, e.g., casts and the11
pseudo-routine sizeof:12
C
A
C
y = (* int)x;
i = sizeof([ 5 ] * int);
y = (int *)x;
i = sizeof(int * [ 5 ]);
13
Finally, new C
A
declarations may appear together with C declarations in the same program block, but14
cannot be mixed within a specific declaration. Therefore, a programmer has the option of either continuing15
to use traditional C declarations or take advantage of the new style. Clearly, both styles need to be supported16
for some time due to existing C-style header-files, particularly for UNIX-like systems.17
13 Pointer / Reference18
C provides a pointer type; C
A
adds a reference type. These types may be derived from an object or routine19
type, called the referenced type. Objects of these types contain an address, which is normally a location in20
memory, but may also address memory-mapped registers in hardware devices. An integer constant expres-21
sion with the value 0, or such an expression cast to type void *, is called a null-pointer constant.10 An address22
is sound, if it points to a valid memory location in scope, i.e., within the program’s execution-environment23
9 The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent
feature. [6, § 6.11.5(1)]10 One way to conceptualize the null pointer is that no variable is placed at this address, so the null-pointer address can be used to
denote an uninitialized pointer/reference object; i.e., the null pointer is guaranteed to compare unequal to a pointer to any object or
routine. In general, a value with special meaning among a set of values is called a sentinel value, e.g., 1 as a return code value.
13 Pointer / Reference 21
and has not been freed. Dereferencing an unsound address, including the null pointer, is undefined, often1
resulting in a memory fault.2
A program object is a region of data storage in the execution environment, the contents of which can3
represent values. In most cases, objects are located in memory at an address, and the variable name for an4
object is an implicit address to the object generated by the compiler and automatically dereferenced, as in:5
int x;
x = 3;
int y;y = x;
3
3104
100x
yint
int int * const x = (int *)100
*x = 3; // implicit dereference
int * const y = (int *)104;
*y = *x; // implicit dereference
6
where the right example is how the compiler logically interprets the variables in the left example. Since a7
variable name only points to one address during its lifetime, it is an immutable pointer; hence, the implicit8
type of pointer variables x and y are constant pointers in the compiler interpretation. In general, variable9
addresses are stored in instructions instead of loaded from memory, and hence may not occupy storage.10
These approaches are contrasted in the following:11
explicit variable address implicit variable address
lda r1,100 // load address of x
ld r2,(r1) // load value of xlda r3,104 // load address of y
st r2,(r3) // store x into y
ld r2,(100) // load value of x
st r2,(104) // store x into y
12
Finally, the immutable nature of a variable’s address and the fact that there is no storage for the variable13
pointer means pointer assignment is impossible. Therefore, the expression x = y has only one meaning,14
*x = *y, i.e., manipulate values, which is why explicitly writing the dereferences is unnecessary even though15
it occurs implicitly as part of instruction decoding.16
A pointer/reference object is a generalization of an object variable-name, i.e., a mutable address that17
can point to more than one memory location during its lifetime. (Similarly, an integer variable can contain18
multiple integer literals during its lifetime versus an integer constant representing a single literal during its19
lifetime, and like a variable name, may not occupy storage if the literal is embedded directly into instruc-20
tions.) Hence, a pointer occupies memory to store its current address, and the pointer’s value is loaded by21
dereferencing, e.g.:22
int x, y, * p1, * p2, ** p3;
p1 = &x; // p1 points to xp2 = p1; // p2 points to x
p1 = &y; // p1 points to y
p3 = &p2; // p3 points to p2 104y
int112
p23 int *100
100x
int108
p13 int *
116p3
104 112 int **
23
Notice, an address has a duality: a location in memory or the value at that location. In many cases,24
a compiler might be able to infer the best meaning for these two cases. For example, Algol68 [24] infers25
pointer dereferencing to select the best meaning for each pointer usage26
p2 = p1 + x; // compiler infers *p2 = *p1 + x;27
Algol68 infers the following dereferencing *p2 = *p1 + x, because adding the arbitrary integer value in x to28
the address of p1 and storing the resulting address into p2 is an unlikely operation. Unfortunately, automatic29
dereferencing does not work in all cases, and so some mechanism is necessary to fix incorrect choices.30
Rather than inferring dereference, most programming languages pick one implicit dereferencing seman-31
tics, and the programmer explicitly indicates the other to resolve address-duality. In C, objects of pointer32
type always manipulate the pointer object’s address:33
p1 = p2; // p1 = p2 rather than *p1 = *p234
p2 = p1 + x; // p2 = p1 + x rather than *p2 = *p1 + x35
22 13 Pointer / Reference
even though the assignment to p2 is likely incorrect, and the programmer probably meant:1
p1 = p2; // pointer address assignment2
*p2 = *p1 + x; // pointed-to value assignment / operation3
The C semantics work well for situations where manipulation of addresses is the primary meaning and data4
is rarely accessed, such as storage management (malloc/free).5
However, in most other situations, the pointed-to value is requested more often than the pointer address.6
*p2 = ((*p1 + *p2) * (**p3 *p1)) / (**p3 15);7
In this case, it is tedious to explicitly write the dereferencing, and error prone when pointer arithmetic is8
allowed. It is better to have the compiler generate the dereferencing and have no implicit pointer arithmetic:9
p2 = ((p1 + p2) * (p3 p1)) / (p3 15);10
To support this common case, a reference type is introduced in C
A
, denoted by &, which is the opposite11
dereference semantics to a pointer type, making the value at the pointed-to location the implicit semantics12
for dereferencing (similar but not the same as C++ reference types).13
int x, y, & r1, & r2, && r3;14
&r1 = &x; // r1 points to x15
&r2 = &r1; // r2 points to x16
&r1 = &y; // r1 points to y17
&&r3 = &&r2; // r3 points to r218
r2 = ((r1 + r2) * (r3 r1)) / (r3 15); // implicit dereferencing19
Except for auto-dereferencing by the compiler, this reference example is the same as the previous pointer20
example. Hence, a reference behaves like the variable name for the current variable it is pointing-to. One21
way to conceptualize a reference is via a rewrite rule, where the compiler inserts a dereference operator22
before the reference variable for each reference qualifier in a declaration, so the previous example becomes:23
*r2 = ((*r1 + *r2) * (**r3 *r1)) / (**r3 15);24
When a reference operation appears beside a dereference operation, e.g., &*, they cancel out. However, in C,25
the cancellation always yields a value (rvalue).11 For a C
A
reference type, the cancellation on the left-hand26
side of assignment leaves the reference as an address (lvalue):27
(&*)r1 = &x; // (&*) cancel giving address in r1 not variable pointed-to by r128
Similarly, the address of a reference can be obtained for assignment or computation (rvalue):29
(&(&*)*)r3 = &(&*)r2; // (&*) cancel giving address in r2, (&(&*)*) cancel giving address in r330
Cancellation works to arbitrary depth.31
Fundamentally, pointer and reference objects are functionally interchangeable because both contain32
addresses.33
int x, *p1 = &x, **p2 = &p1, ***p3 = &p2,34
&r1 = x, &&r2 = r1, &&&r3 = r2;35
***p3 = 3; // change x36
r3 = 3; // change x, ***r337
**p3 = ...; // change p138
&r3 = ...; // change r1, (&*)**r3, 1 cancellation39
*p3 = ...; // change p240
&&r3 = ...; // change r2, (&(&*)*)*r3, 2 cancellations41
&&&r3 = p3; // change r3 to p3, (&(&(&*)*)*)r3, 3 cancellations42
11 The unary & operator yields the address of its operand. If the operand has type “type”, the result has type “pointer to type”. If
the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and the result is not an lvalue. [6, § 6.5.3.2–3]
13.1 Initialization 23
Furthermore, both types are equally performant, as the same amount of dereferencing occurs for both types.1
Therefore, the choice between them is based solely on whether the address is dereferenced frequently or2
infrequently, which dictates the amount of implicit dereferencing aid from the compiler.3
As for a pointer type, a reference type may have qualifiers:4
const int cx = 5; // cannot change cx;5
const int & cr = cx; // cannot change what cr points to6
&cr = &cx; // can change cr7
cr = 7; // error, cannot change cx8
int & const rc = x; // must be initialized9
&rc = &x; // error, cannot change rc10
const int & const crc = cx; // must be initialized11
crc = 7; // error, cannot change cx12
&crc = &cx; // error, cannot change crc13
Hence, for type & const, there is no pointer assignment, so &rc = &x is disallowed, and the address value14
cannot be the null pointer unless an arbitrary pointer is coerced into the reference:15
int & const cr = *0; // where 0 is the int * zero16
Note, constant reference-types do not prevent addressing errors because of explicit storage-management:17
int & const cr = *malloc();18
cr = 5;19
free( &cr );20
cr = 7; // unsound pointer dereference21
The position of the const qualifier after the pointer/reference qualifier causes confuse for C program-22
mers. The const qualifier cannot be moved before the pointer/reference qualifier for C style-declarations;23
C
A
-style declarations (see Section 12, p. 19) attempt to address this issue:24
CA
C
const * const * const int ccp;const & const & const int ccr;
const int * const * const ccp;25
where the C
A
declaration is read left-to-right.26
Finally, like pointers, references are usable and composable with other type operators and generators.27
int w, x, y, z, & ar[3] = { x, y, z }; // initialize array of references28
&ar[1] = &w; // change reference array element29
typeof( ar[1] ) p; // (gcc) is int, i.e., the type of referenced object30
typeof( &ar[1] ) q; // (gcc) is int &, i.e., the type of reference31
sizeof( ar[1] ) == sizeof( int ); // is true, i.e., the size of referenced object32
sizeof( &ar[1] ) == sizeof( int *) // is true, i.e., the size of a reference33
In contrast to C
A
reference types, C++’s reference types are all const references, preventing changes to34
the reference address, so only value assignment is possible, which eliminates half of the address duality.35
Also, C++ does not allow arrays of reference12 Java’s reference types to objects (all Java objects are on the36
heap) are like C pointers, which always manipulate the address, and there is no (bit-wise) object assignment,37
so objects are explicitly cloned by shallow or deep copying, which eliminates half of the address duality.38
13.1 Initialization39
Initialization is different than assignment because initialization occurs on the empty (uninitialized) storage40
on an object, while assignment occurs on possibly initialized storage of an object. There are three initial-41
12 The reason for disallowing arrays of reference is unknown, but possibly comes from references being ethereal (like a textual
macro), and hence, replaceable by the referant object.
24 13.1 Initialization
ization contexts in C
A
: declaration initialization, argument/parameter binding, return/temporary binding.1
Because the object being initialized has no value, there is only one meaningful semantics with respect to2
address duality: it must mean address as there is no pointed-to value. In contrast, the left-hand side of3
assignment has an address that has a duality. Therefore, for pointer/reference initialization, the initializing4
value must be an address not a value.5
int * p = &x; // assign address of x6
int * p = x; // assign value of x7
int & r = x; // must have address of x8
Like the previous example with C pointer-arithmetic, it is unlikely assigning the value of x into a pointer is9
meaningful (again, a warning is usually given). Therefore, for safety, this context requires an address, so10
it is superfluous to require explicitly taking the address of the initialization object, even though the type is11
incorrect. Note, this is strictly a convenience and safety feature for a programmer. Hence, C
A
allows r to12
be assigned x because it infers a reference for x, by implicitly inserting a address-of operator, &, and it is an13
error to put an & because the types no longer match due to the implicit dereference. Unfortunately, C allows14
p to be assigned with &x (address) or x (value), but most compilers warn about the latter assignment as15
being potentially incorrect. Similarly, when a reference type is used for a parameter/return type, the call-site16
argument does not require a reference operator for the same reason.17
int & f( int & r ); // reference parameter and return18
z = f( x ) + f( y ); // reference operator added, temporaries needed for call results19
Within routine f, it is possible to change the argument by changing the corresponding parameter, and param-20
eter r can be locally reassigned within f. Since operator routine ?+? takes its arguments by value, the21
references returned from f are used to initialize compiler generated temporaries with value semantics that22
copy from the references.23
int temp1 = f( x ), temp2 = f( y );24
z = temp1 + temp2;25
This implicit referencing is crucial for reducing the syntactic burden for programmers when using refer-26
ences; otherwise references have the same syntactic burden as pointers in these contexts.27
When a pointer/reference parameter has a const value (immutable), it is possible to pass literals and28
expressions.29
void f( const int & cr );30
void g( const int * cp );31
f( 3 ); g( &3 );32
f( x + y ); g( &(x + y) );33
Here, the compiler passes the address to the literal 3 or the temporary for the expression x + y, knowing the34
argument cannot be changed through the parameter. The & before the constant/expression for the pointer-35
type parameter (g) is a C
A
extension necessary to type match and is a common requirement before a variable36
in C (e.g., scanf). Importantly, &3 may not be equal to &3, where the references occur across calls because37
the temporaries maybe different on each call.38
C
A
extends this semantics to a mutable pointer/reference parameter, and the compiler implicitly creates39
the necessary temporary (copying the argument), which is subsequently pointed-to by the reference param-40
eter and can be changed.1341
void f( int & r );42
void g( int * p );43
f( 3 ); g( &3 ); // compiler implicit generates temporaries44
f( x + y ); g( &(x + y) ); // compiler implicit generates temporaries45
13 If whole program analysis is possible, and shows the parameter is not assigned, i.e., it is const, the temporary is unnecessary.
13.2 Address-of Semantics 25
Essentially, there is an implicit rvalue to lvalue conversion in this case.14 The implicit conversion allows1
seamless calls to any routine without having to explicitly name/copy the literal/expression to allow the call.2
Finally, C handles routine objects in an inconsistent way. A routine object is both a pointer and a3
reference (particle and wave).4
void f( int i );5
void (* fp)( int ); // routine pointer6
fp = f; // reference initialization7
fp = &f; // pointer initialization8
fp = *f; // reference initialization9
fp(3); // reference invocation10
(*fp)(3); // pointer invocation11
While C’s treatment of routine objects has similarity to inferring a reference type in initialization contexts,12
the examples are assignment not initialization, and all possible forms of assignment are possible (f, &f, *f)13
without regard for type. Instead, a routine object should be referenced by a const reference:14
const void (& fr)( int ) = f; // routine reference15
fr = ... // error, cannot change code16
&fr = ...; // changing routine reference17
fr( 3 ); // reference call to f18
(*fr)(3); // error, incorrect type19
because the value of the routine object is a routine literal, i.e., the routine code is normally immutable20
during execution.15 C
A
allows this additional use of references for routine objects in an attempt to give a21
more consistent meaning for them.22
13.2 Address-of Semantics23
In C, &E is an rvalue for any expression E. CA
extends the & (address-of) operator as follows:24
• if R is an rvalue of type T &1 · · · &r, where r ≥ 1 references (& symbols), than &R has type T *&2 · · · &r,25
i.e., T pointer with r−1 references (& symbols).26
• if L is an lvalue of type T &1 · · · &l , where l ≥ 0 references (& symbols), than &L has type T *&1 · · · &l ,27
i.e., T pointer with l references (& symbols).28
The following example shows the first rule applied to different rvalue contexts:29
int x, * px, ** ppx, *** pppx, **** ppppx;30
int & rx = x, && rrx = rx, &&& rrrx = rrx ;31
x = rrrx; // rrrx is an lvalue with type int &&& (equivalent to x)32
px = &rrrx; // starting from rrrx, &rrrx is an rvalue with type int *&&& (&x)33
ppx = &&rrrx; // starting from &rrrx, &&rrrx is an rvalue with type int **&& (&rx)34
pppx = &&&rrrx; // starting from &&rrrx, &&&rrrx is an rvalue with type int ***& (&rrx)35
ppppx = &&&&rrrx; // starting from &&&rrrx, &&&&rrrx is an rvalue with type int **** (&rrrx)36
The following example shows the second rule applied to different lvalue contexts:37
int x, * px, ** ppx, *** pppx;38
int & rx = x, && rrx = rx, &&& rrrx = rrx ;39
rrrx = 2; // rrrx is an lvalue with type int &&& (equivalent to x)40
&rrrx = px; // starting from rrrx, &rrrx is an rvalue with type int *&&& (rx)41
&&rrrx = ppx; // starting from &rrrx, &&rrrx is an rvalue with type int **&& (rrx)42
14 This conversion attempts to address the const hell problem, when the innocent addition of a const qualifier causes a cascade of
type failures, requiring an unknown number of additional const qualifiers, until it is discovered a const qualifier cannot be added
and all the const qualifiers must be removed.15 Dynamic code rewriting is possible but only in special circumstances.
26 14 Routine Definition
&&&rrrx = pppx; // starting from &&rrrx, &&&rrrx is an rvalue with type int ***& (rrrx)1
13.3 Conversions2
C provides a basic implicit conversion to simplify variable usage:3
0. lvalue to rvalue conversion: cv T converts to T, which allows implicit variable dereferencing.4
int x;5
x + 1; // lvalue variable (int) converts to rvalue for expression6
An rvalue has no type qualifiers (cv), so the lvalue qualifiers are dropped.7
C
A
provides three new implicit conversion for reference types to simplify reference usage.8
1. reference to rvalue conversion: cv T & converts to T, which allows implicit reference dereferencing.9
int x, &r = x, f( int p );10
x = r + f( r ); // lvalue reference converts to rvalue11
An rvalue has no type qualifiers (cv), so the reference qualifiers are dropped.12
2. lvalue to reference conversion: lvalue type cv1 T converts to cv2 T &, which allows implicitly converting13
variables to references.14
int x, &r = x, f( int & p ); // lvalue variable (int) convert to reference (int &)15
f( x ); // lvalue variable (int) convert to reference (int &)16
Conversion can restrict a type, where cv1 ≤ cv2, e.g., passing an int to a const volatile int &, which17
has low cost. Conversion can expand a type, where cv1 > cv2, e.g., passing a const volatile int to an18
int &, which has high cost (warning); furthermore, if cv1 has const but not cv2, a temporary variable19
is created to preserve the immutable lvalue.20
3. rvalue to reference conversion: T converts to cv T &, which allows binding references to temporaries.21
int x, & f( int & p );22
f( x + 3 ); // rvalue parameter (int) implicitly converts to lvalue temporary reference (int &)23
&f(...) = &x; // rvalue result (int &) implicitly converts to lvalue temporary reference (int &)24
In both case, modifications to the temporary are inaccessible (warning). Conversion expands the25
temporary-type with cv, which is low cost since the temporary is inaccessible.26
14 Routine Definition27
C
A
also supports a new syntax for routine definition, as well as C11 and K&R routine syntax. The point of28
the new syntax is to allow returning multiple values from a routine [18, 25], e.g.:29
[ int o1, int o2, char o3 ] f( int i1, char i2, char i3 ) {30
routine body31
}32
where routine f has three output (return values) and three input parameters. Existing C syntax cannot be33
extended with multiple return types because it is impossible to embed a single routine name within multiple34
return type specifications.35
In detail, the brackets, [], enclose the result type, where each return value is named and that name is a36
local variable of the particular return type.16 The value of each local return variable is automatically returned37
at routine termination. Declaration qualifiers can only appear at the start of a routine definition, e.g.:38
16 Michael Tiemann, with help from Doug Lea, provided named return values in g++, circa 1989.
14.1 Named Return Values 27
extern [ int x ] g( int y ) { }1
Lastly, if there are no output parameters or input parameters, the brackets and/or parentheses must still be2
specified; in both cases the type is assumed to be void as opposed to old style C defaults of int return type3
and unknown parameter types, respectively, as in:4
[ ] g(); // no input or output parameters5
[ void ] g( void ); // no input or output parameters6
Routine f is called as follows:7
[ i, j, ch ] = f( 3, 'a', ch );8
The list of return values from f and the grouping on the left-hand side of the assignment is called a return9
list and discussed in Section 12.10
C
A
style declarations cannot be used to declare parameters for K&R style routine definitions because of11
the following ambiguity:12
int (*f(x))[ 5 ] int x; {}13
The string “int (*f(x))[ 5 ]” declares a K&R style routine of type returning a pointer to an array of 5 integers,14
while the string “[ 5 ] int x” declares a C
A
style parameter x of type array of 5 integers. Since the strings15
overlap starting with the open bracket, [, there is an ambiguous interpretation for the string. As well, C
A
-style16
declarations cannot be used to declare parameters for C-style routine-definitions because of the following17
ambiguity:18
typedef int foo;19
int f( int (* foo) ); // foo is redefined as a parameter name20
The string “int (* foo)” declares a C-style named-parameter of type pointer to an integer (the parenthesis are21
superfluous), while the same string declares a C
A
style unnamed parameter of type routine returning integer22
with unnamed parameter of type pointer to foo. The redefinition of a type name in a parameter list is the23
only context in C where the character * can appear to the left of a type name, and CA
relies on all type24
qualifier characters appearing to the right of the type name. The inability to use C
A
declarations in these25
two contexts is probably a blessing because it precludes programmers from arbitrarily switching between26
declarations forms within a declaration contexts.27
C-style declarations can be used to declare parameters for C
A
style routine definitions, e.g.:28
[ int ] f( * int, int * ); // returns an integer, accepts 2 pointers to integers29
[ * int, int * ] f( int ); // returns 2 pointers to integers, accepts an integer30
The reason for allowing both declaration styles in the new context is for backwards compatibility with31
existing preprocessor macros that generate C-style declaration-syntax, as in:32
#define ptoa( n, d ) int (*n)[ d ]33
int f( ptoa( p, 5 ) ) ... // expands to int f( int (*p)[ 5 ] )34
[ int ] f( ptoa( p, 5 ) ) ... // expands to [ int ] f( int (*p)[ 5 ] )35
Again, programmers are highly encouraged to use one declaration form or the other, rather than mixing the36
forms.37
14.1 Named Return Values38
Named return values handle the case where it is necessary to define a local variable whose value is then39
returned in a return statement, as in:40
int f() {41
int x;42
... x = 0; ... x = y; ...43
return x;44
28 14.2 Routine Prototype
}1
Because the value in the return variable is automatically returned when a C
A
routine terminates, the return2
statement does not contain an expression, as in:3
[ int x, int y ] f() {
int z;... x = 0; ... y = z; ...
return; // implicitly return x, y
}
4
When the return is encountered, the current values of x and y are returned to the calling routine. As well,5
“falling off the end” of a routine without a return statement is permitted, as in:6
[ int x, int y ] f() {7
...8
} // implicitly return x, y9
In this case, the current values of x and y are returned to the calling routine just as if a return had been10
encountered.11
Named return values may be used in conjunction with named parameter values; specifically, a return and12
parameter can have the same name.13
[ int x, int y ] f( int, x, int y ) {14
...15
} // implicitly return x, y16
This notation allows the compiler to eliminate temporary variables in nested routine calls.17
[ int x, int y ] f( int, x, int y ); // prototype declaration18
int a, b;19
[a, b] = f( f( f( a, b ) ) );20
While the compiler normally ignores parameters names in prototype declarations, here they are used to21
eliminate temporary return-values by inferring that the results of each call are the inputs of the next call,22
and ultimately, the left-hand side of the assignment. Hence, even without the body of routine f (separate23
compilation), it is possible to perform a global optimization across routine calls. The compiler warns about24
naming inconsistencies between routine prototype and definition in this case, and behaviour is undefined if25
the programmer is inconsistent.26
14.2 Routine Prototype27
The syntax of the new routine prototype declaration follows directly from the new routine definition syntax;28
as well, parameter names are optional, e.g.:29
[ int x ] f (); // returning int with no parameters30
[ * int ] g (int y); // returning pointer to int with int parameter31
[ ] h ( int, char ); // returning no result with int and char parameters32
[ * int, int ] j ( int ); // returning pointer to int and int, with int parameter33
This syntax allows a prototype declaration to be created by cutting and pasting source text from the routine34
definition header (or vice versa). Like C, it is possible to declare multiple routine-prototypes in a single35
declaration, where the return type is distributed across all routine names in the declaration list (see Section 12,36
p. 19), e.g.:37
C : const double bar1(), bar2( int ), bar3( double );38
C
A
: [const double] foo(), foo( int ), foo( double ) { return 3.0; }39
C
A
allows the last routine in the list to define its body.40
Declaration qualifiers can only appear at the start of a C
A
routine declaration,9 e.g.:41
15 Routine Pointers 29
extern [ int ] f ( int );1
static [ int ] g ( int );2
15 Routine Pointers3
The syntax for pointers to C
A
routines specifies the pointer name on the right, e.g.:4
* [ int x ] () fp; // pointer to routine returning int with no parameters5
* [ * int ] (int y) gp; // pointer to routine returning pointer to int with int parameter6
* [ ] (int,char) hp; // pointer to routine returning no result with int and char parameters7
* [ * int,int ] ( int ) jp; // pointer to routine returning pointer to int and int, with int parameter8
While parameter names are optional, a routine name cannot be specified; for example, the following is9
incorrect:10
* [ int x ] f () fp; // routine name "f" is not allowed11
16 Named and Default Arguments12
Named and default arguments [22]17 are two mechanisms to simplify routine call. Both mechanisms are13
discussed with respect to C
A
.14
Named (or Keyword) Arguments: provide the ability to specify an argument to a routine call using the15
parameter name rather than the position of the parameter. For example, given the routine:16
void p( int x, int y, int z ) {...}17
a positional call is:18
p( 4, 7, 3 );19
whereas a named (keyword) call may be:20
p( z : 3, x : 4, y : 7 ); // rewrite⇒ p( 4, 7, 3 )21
Here the order of the arguments is unimportant, and the names of the parameters are used to associate22
argument values with the corresponding parameters. The compiler rewrites a named call into a positional23
call. The advantages of named parameters are:24
• Remembering the names of the parameters may be easier than the order in the routine definition.25
• Parameter names provide documentation at the call site (assuming the names are descriptive).26
• Changes can be made to the order or number of parameters without affecting the call (although the27
call must still be recompiled).28
Unfortunately, named arguments do not work in C-style programming-languages because a routine29
prototype is not required to specify parameter names, nor do the names in the prototype have to match30
with the actual definition. For example, the following routine prototypes and definition are all valid.31
void p( int, int, int ); // equivalent prototypes32
void p( int x, int y, int z );33
void p( int y, int x, int z );34
void p( int z, int y, int x );35
void p( int q, int r, int s ) {} // match with this definition36
17 Francez [17] proposed a further extension to the named-parameter passing style, which specifies what type of communication (by
value, by reference, by name) the argument is passed to the routine.
30 16 Named and Default Arguments
Forcing matching parameter names in routine prototypes with corresponding routine definitions is possible,1
but goes against a strong tradition in C programming. Alternatively, prototype definitions can be elim-2
inated by using a two-pass compilation, and implicitly creating header files for exports. The former is3
easy to do, while the latter is more complex.4
Furthermore, named arguments do not work well in a C
A
-style programming-languages because5
they potentially introduces a new criteria for type matching. For example, it is technically possible to6
disambiguate between these two overloaded definitions of f based on named arguments at the call site:7
int f( int i, int j );8
int f( int x, double y );9
10
f( j : 3, i : 4 ); // 1st f11
f( x : 7, y : 8.1 ); // 2nd f12
f( 4, 5 ); // ambiguous call13
However, named arguments compound routine resolution in conjunction with conversions:14
f( i : 3, 5.7 ); // ambiguous call ?15
Depending on the cost associated with named arguments, this call could be resolvable or ambiguous.16
Adding named argument into the routine resolution algorithm does not seem worth the complexity.17
Therefore, C
A
does not attempt to support named arguments.18
Default Arguments provide the ability to associate a default value with a parameter so it can be optionally19
specified in the argument list. For example, given the routine:20
void p( int x = 1, int y = 2, int z = 3 ) {...}21
the allowable positional calls are:22
p(); // rewrite⇒ p( 1, 2, 3 )23
p( 4 ); // rewrite⇒ p( 4, 2, 3 )24
p( 4, 4 ); // rewrite⇒ p( 4, 4, 3 )25
p( 4, 4, 4 ); // rewrite⇒ p( 4, 4, 4 )26
// empty arguments27
p( , 4, 4 ); // rewrite⇒ p( 1, 4, 4 )28
p( 4, , 4 ); // rewrite⇒ p( 4, 2, 4 )29
p( 4, 4, ); // rewrite⇒ p( 4, 4, 3 )30
p( 4, , ); // rewrite⇒ p( 4, 2, 3 )31
p( , 4, ); // rewrite⇒ p( 1, 4, 3 )32
p( , , 4 ); // rewrite⇒ p( 1, 2, 4 )33
p( , , ); // rewrite⇒ p( 1, 2, 3 )34
Here the missing arguments are inserted from the default values in the parameter list. The compiler35
rewrites missing default values into explicit positional arguments. The advantages of default values are:36
• Routines with a large number of parameters are often very generalized, giving a programmer a37
number of different options on how a computation is performed. For many of these kinds of routines,38
there are standard or default settings that work for the majority of computations. Without default39
values for parameters, a programmer is forced to specify these common values all the time, resulting40
in long argument lists that are error prone.41
• When a routine’s interface is augmented with new parameters, it extends the interface providing42
generalizability18 (somewhat like the generalization provided by inheritance for classes). That is, all43
existing calls are still valid, although the call must still be recompiled.44
18 “It should be possible for the implementor of an abstraction to increase its generality. So long as the modified abstraction is a
16 Named and Default Arguments 31
The only disadvantage of default arguments is that unintentional omission of an argument may not result1
in a compiler-time error. Instead, a default value is used, which may not be the programmer’s intent.2
Default values may only appear in a prototype versus definition context:3
void p( int x, int y = 2, int z = 3 ); // prototype: allowed4
void p( int, int = 2, int = 3 ); // prototype: allowed5
void p( int x, int y = 2, int z = 3 ) {} // definition: not allowed6
The reason for this restriction is to allow separate compilation. Multiple prototypes with different default7
values is an error.8
Ellipse (“...”) arguments present problems when used with default arguments. The conflict occurs9
because both named and ellipse arguments must appear after positional arguments, giving two possibilities:10
p( /* positional */, ... , /* named */ );11
p( /* positional */, /* named */, ... );12
While it is possible to implement both approaches, the first possibly is more complex than the second, e.g.:13
p( int x, int y, int z, ... );14
p( 1, 4, 5, 6, z : 3, y : 2 ); // assume p( /* positional */, ... , /* named */ );15
p( 1, z : 3, y : 2, 4, 5, 6 ); // assume p( /* positional */, /* named */, ... );16
In the first call, it is necessary for the programmer to conceptually rewrite the call, changing named argu-17
ments into positional, before knowing where the ellipse arguments begin. Hence, this approach seems18
significantly more difficult, and hence, confusing and error prone. In the second call, the named arguments19
separate the positional and ellipse arguments, making it trivial to read the call.20
The problem is exacerbated with default arguments, e.g.:21
void p( int x, int y = 2, int z = 3... );22
p( 1, 4, 5, 6, z : 3 ); // assume p( /* positional */, ... , /* named */ );23
p( 1, z : 3, 4, 5, 6 ); // assume p( /* positional */, /* named */, ... );24
The first call is an error because arguments 4 and 5 are actually positional not ellipse arguments; therefore,25
argument 5 subsequently conflicts with the named argument z : 3. In the second call, the default value26
for y is implicitly inserted after argument 1 and the named arguments separate the positional and ellipse27
arguments, making it trivial to read the call. For these reasons, C
A
requires named arguments before ellipse28
arguments. Finally, while ellipse arguments are needed for a small set of existing C routines, like printf, the29
extended C
A
type system largely eliminates the need for ellipse arguments (see Section 24), making much30
of this discussion moot.31
Default arguments and overloading (see Section 24) are complementary. While in theory default argu-32
ments can be simulated with overloading, as in:33
default arguments overloading
void p( int x, int y = 2, int z = 3 ) {...} void p( int x, int y, int z ) {...}void p( int x ) { p( x, 2, 3 ); }
void p( int x, int y ) { p( x, y, 3 ); }
34
the number of required overloaded routines is linear in the number of default values, which is unacceptable35
growth. In general, overloading should only be used over default arguments if the body of the routine is36
significantly different. Furthermore, overloading cannot handle accessing default arguments in the middle37
of a positional list, via a missing argument, such as:38
generalization of the original, existing uses of the abstraction will not require change. It might be possible to modify an abstraction
in a manner which is not a generalization without affecting existing uses, but, without inspecting the modules in which the uses
occur, this possibility cannot be determined. This criterion precludes the addition of parameters, unless these parameters have
default or inferred values that are valid for all possible existing applications.” [10, p. 128]
32 18.2 Routine Nesting
p( 1, /* default */, 5 ); // rewrite⇒ p( 1, 2, 5 )1
Given the C
A
restrictions above, both named and default arguments are backwards compatible. C++ only2
supports default arguments; Ada supports both named and default arguments.3
17 Unnamed Structure Fields4
C requires each field of a structure to have a name, except for a bit field associated with a basic type, e.g.:5
struct {6
int f1; // named field7
int f2 : 4; // named field with bit field size8
int : 3; // unnamed field for basic type with bit field size9
int ; // disallowed, unnamed field10
int *; // disallowed, unnamed field11
int (*)( int ); // disallowed, unnamed field12
};13
This requirement is relaxed by making the field name optional for all field declarations; therefore, all the14
field declarations in the example are allowed. As for unnamed bit fields, an unnamed field is used for15
padding a structure to a particular size. A list of unnamed fields is also supported, e.g.:16
struct {17
int , , ; // 3 unnamed fields18
}19
18 Nesting20
Nesting of types and routines is useful for controlling name visibility (name hiding).21
18.1 Type Nesting22
C
A
allows type nesting, and type qualification of the nested types (see Figure 4), where as C hoists (refactors)23
nested types into the enclosing scope and has no type qualification. In the left example in C, types C, U and24
T are implicitly hoisted outside of type S into the containing block scope. In the right example in C
A
, the25
types are not hoisted and accessed using the field-selection operator “.” for type qualification, as does Java,26
rather than the C++ type-selection operator “::”.27
18.2 Routine Nesting28
While C
A
does not provide object programming by putting routines into structures, it does rely heavily on29
locally nested routines to redefine operations at or close to a call site. For example, the C quick-sort is30
wrapped into the following polymorphic C
A
routine:31
forall( otype T | { int ?<?( T, T ); } )32
void qsort( const T * arr, size t dimension );33
which can be used to sort in ascending and descending order by locally redefining the less-than operator into34
greater-than.35
const unsigned int size = 5;36
int ia[size];37
... // assign values to array ia38
qsort( ia, size ); // sort ascending order using builtin ?<?39
{40
19 Tuple 33
C Type Nesting C Implicit Hoisting C
A
struct S {enum C { R, G, B };
struct T {
union U { int i, j; };enum C c;
short int i, j;
};struct T t;
} s;
int fred() {
s.t.c = R;struct T t = { R, 1, 2 };
enum C c;
union U u;}
enum C { R, G, B };union U { int i, j; };
struct T {
enum C c;short int i, j;
};
struct S {struct T t;
} s;
struct S {enum C { R, G, B };
struct T {
union U { int i, j; };enum C c;
short int i, j;
};struct T t;
} s;
int fred() {
s.t.c = S.R; // type qualificationstruct S.T t = { S.R, 1, 2 };
enum S.C c;
union S.T.U u;}
Figure 4: Type Nesting / Qualification
int ?<?( int x, int y ) { return x > y; } // nested routine1
qsort( ia, size ); // sort descending order by local redefinition2
}3
Nested routines are not first-class, meaning a nested routine cannot be returned if it has references to4
variables in its enclosing blocks; the only exception is references to the external block of the translation unit,5
as these variables persist for the duration of the program. The following program in undefined in CA
(and6
Indexcgcc)7
[* [int]( int )] foo() { // int (* foo())( int )8
int i = 7;9
int bar( int p ) {10
i += 1; // dependent on local variable11
sout | i;12
}13
return bar; // undefined because of local dependence14
}15
int main() {16
* [int]( int ) fp = foo(); // int (* fp)( int )17
sout | fp( 3 );18
}19
because20
Currently, there are no lambda expressions, i.e., unnamed routines because routine names are very21
important to properly select the correct routine.22
19 Tuple23
In C and C
A
, lists of elements appear in several contexts, such as the parameter list of a routine call.24
f( 2, x, 3 + i ); // element list25
A list of elements is called a tuple, and is different from a comma expression.26
34 19.1 Multiple-Return-Value Functions
19.1 Multiple-Return-Value Functions1
In C and most programming languages, functions return at most one value; however, many operations have2
multiple outcomes, some exceptional (see Section 11, p. 18). To emulate functions with multiple return3
values, aggregation and/or aliasing is used.4
In the former approach, a record type is created combining all of the return values. For example, consider5
C’s div function, which returns the quotient and remainder for a division of an integer value.6
typedef struct { int quot, rem; } div t; // from include stdlib.h7
div t div( int num, int den );8
div t qr = div( 13, 5 ); // return quotient/remainder aggregate9
printf( "%d %d\n", qr.quot, qr.rem ); // print quotient/remainder10
This approach requires a name for the return type and fields, where naming is a common programming-11
language issue. That is, naming creates an association that must be managed when reading and writing12
code. While effective when used sparingly, this approach does not scale when functions need to return13
multiple combinations of types.14
In the latter approach, additional return values are passed as pointer parameters. A pointer parameter is15
assigned inside the routine to emulate a return. For example, consider C’s modf function, which returns the16
integral and fractional part of a floating value.17
double modf( double x, double * i ); // from include math.h18
double intp, frac = modf( 13.5, &intp ); // return integral and fractional components19
printf( "%g %g\n", intp, frac ); // print integral/fractional components20
This approach requires allocating storage for the return values, which complicates the call site with a21
sequence of variable declarations leading to the call. Also, while a disciplined use of const can give clues22
about whether a pointer parameter is used as an out parameter, it is not obvious from the routine signature23
whether the callee expects such a parameter to be initialized before the call. Furthermore, while many C24
routines that accept pointers are safe for a NULL argument, there are many C routines that are not null-25
safe. Finally, C does not provide a mechanism to state that a parameter is going to be used as an additional26
return value, which makes the job of ensuring that a value is returned more difficult for the compiler. Still,27
not every routine with multiple return values should be required to return an error code, and error codes28
are easily ignored, so this is not a satisfying solution. As with the previous approach, this technique can29
simulate multiple return values, but in practice it is verbose and error prone.30
C
A
allows functions to return multiple values by extending the function declaration syntax. Multiple31
return values are declared as a comma-separated list of types in square brackets in the same location that the32
return type appears in standard C function declarations.33
[ char, int, double ] f( ... );34
The ability to return multiple values from a function requires a new syntax for the return statement. For35
consistency, the return statement in C
A
accepts a comma-separated list of expressions in square brackets.36
return [ c, i, d ];37
The expression resolution ensures the correct form is used depending on the values being returned and38
the return type of the current function. A multiple-returning function with return type T can return any39
expression that is implicitly convertible to T.40
A common use of a function’s output is input to another function. C
A
allows this case, without any new41
syntax; a multiple-returning function can be used in any of the contexts where an expression is allowed.42
When a function call is passed as an argument to another call, the best match of actual arguments to formal43
parameters is evaluated given all possible expression interpretations in the current scope.44
void g( int, int ); // 145
void g( double, double ); // 246
19.2 Expressions 35
g( div( 13, 5 ) ); // select 11
g( modf( 13.5 ) ); // select 22
In this case, there are two overloaded g routines. Both calls to g expect two arguments that are matched3
by the two return values from div and modf. respectively, which are fed directly to the first and second4
parameters of g. As well, both calls to g have exact type matches for the two different versions of g, so these5
exact matches are chosen. When type matches are not exact, conversions are used to find a best match.6
The previous examples can be rewritten passing the multiple returned-values directly to the printf func-7
tion call.8
[ int, int ] div( int x, int y ); // from include stdlib9
printf( "%d %d\n", div( 13, 5 ) ); // print quotient/remainder10
11
[ double, double ] modf( double x ); // from include math12
printf( "%g %g\n", modf( 13.5 ) ); // print integral/fractional components13
This approach provides the benefits of compile-time checking for appropriate return statements as in aggre-14
gation, but without the required verbosity of declaring a new named type.15
Finally, the addition of multiple-return-value functions necessitates a syntax for retaining the multiple16
values at the call-site versus their temporary existence during a call. The simplest mechanism for retaining17
a return value in C is variable assignment. By assigning the multiple return-values into multiple variables,18
the values can be retrieved later. As such, C
A
allows assigning multiple values from a function into multiple19
variables, using a square-bracketed list of lvalue expressions on the left side.20
int quot, rem;21
[ quot, rem ] = div( 13, 5 ); // assign multiple variables22
printf( "%d %d\n", quot, rem ); // print quotient/remainder23
Here, the multiple return-values are matched in much the same way as passing multiple return-values to24
multiple parameters in a call.25
19.2 Expressions26
Multiple-return-value functions provide C
A
with a new syntax for expressing a combination of expressions27
in the return statement and a combination of types in a function signature. These notions are generalized28
to provide C
A
with tuple expressions and tuple types. A tuple expression is an expression producing a29
fixed-size, ordered list of values of heterogeneous types. The type of a tuple expression is the tuple of the30
subexpression types, or a tuple type.31
In C
A
, a tuple expression is denoted by a comma-separated list of expressions enclosed in square32
brackets. For example, the expression [5, 'x', 10.5] has type [int, char, double]. The previous expres-33
sion has 3 components. Each component in a tuple expression can be any C
A
expression, including another34
tuple expression. The order of evaluation of the components in a tuple expression is unspecified, to allow a35
compiler the greatest flexibility for program optimization. It is, however, guaranteed that each component of36
a tuple expression is evaluated for side-effects, even if the result is not used. Multiple-return-value functions37
can equivalently be called tuple-returning functions.38
19.3 Variables39
The previous call of div still requires the preallocation of multiple return-variables in a manner similar to the40
aliasing example. In C
A
, it is possible to overcome this restriction by declaring a tuple variable.41
[int, int] qr = div( 13, 5 ); // initialize tuple variable42
printf( "%d %d\n", qr ); // print quotient/remainder43
36 19.5 Flattening and Structuring
It is now possible to match the multiple return-values to a single variable, in much the same way as aggre-1
gation. As well, the components of the tuple value are passed as separate parameters to printf, allowing2
direct printing of tuple variables. One way to access the individual components of a tuple variable is with3
assignment.4
[ quot, rem ] = qr; // assign multiple variables5
In addition to variables of tuple type, it is also possible to have pointers to tuples, and arrays of tuples.6
Tuple types can be composed of any types, except for array types, since array assignment is disallowed,7
which makes tuple assignment difficult when a tuple contains an array.8
[ double, int ] di;9
[ double, int ] * pdi10
[ double, int ] adi[10];11
This examples declares a variable of type [double, int], a variable of type pointer to [double, int], and an12
array of ten [double, int].13
19.4 Indexing14
It is also possible to access a single component of a tuple-valued expression without creating temporary15
variables. Given a tuple-valued expression enp and a compile-time constant integer i where 0≤ i < n, where16
n is the number of components in e, e.i accesses the i th component of e, e.g.:17
[int, double] x;18
[char *, int] f();19
void g(double, int);20
[int, double] * p;21
22
int y = x.0; // access int component of x23
y = f().1; // access int component of f24
p >0 = 5; // access int component of tuple pointed-to by p25
g( x.1, x.0 ); // rearrange x to pass to g26
double z = [ x, f() ].0.1; // access second component of first component of tuple expression27
Tuple-index expressions can occur on any tuple-typed expression, including tuple-returning functions, square-28
bracketed tuple expressions, and other tuple-index expressions, provided the retrieved component is also a29
tuple. This feature was proposed for K-W C but never implemented [29, p. 45].30
19.5 Flattening and Structuring31
As evident in previous examples, tuples in C
A
do not have a rigid structure. In function call contexts, tuples32
support implicit flattening and restructuring conversions. Tuple flattening recursively expands a tuple into33
the list of its basic components. Tuple structuring packages a list of expressions into a value of tuple type.34
int f(int, int);35
int g([int, int]);36
int h(int, [int, int]);37
[int, int] x;38
int y;39
40
f(x); // flatten41
g(y, 10); // structure42
h(x, y); // flatten & structure43
19.6 Assignment 37
In C
A
, each of these calls is valid. In the call to f, x is implicitly flattened so that the components of x are1
passed as the two arguments to f. For the call to g, the values y and 10 are structured into a single argument2
of type [int, int] to match the type of the parameter of g. Finally, in the call to h, x is flattened to yield an3
argument list of length 3, of which the first component of x is passed as the first parameter of h, and the4
second component of x and y are structured into the second argument of type [int, int]. The flexible structure5
of tuples permits a simple and expressive function-call syntax to work seamlessly with both single- and6
multiple-return-value functions, and with any number of arguments of arbitrarily complex structure.7
In K-W C [5, 29], there were 4 tuple coercions: opening, closing, flattening, and structuring. Opening8
coerces a tuple value into a tuple of values, while closing converts a tuple of values into a single tuple value.9
Flattening coerces a nested tuple into a flat tuple, i.e., it takes a tuple with tuple components and expands it10
into a tuple with only non-tuple components. Structuring moves in the opposite direction, i.e., it takes a flat11
tuple value and provides structure by introducing nested tuple components.12
In C
A
, the design has been simplified to require only the two conversions previously described, which13
trigger only in function call and return situations. This simplification is a primary contribution of this thesis14
to the design of tuples in C
A
. Specifically, the expression resolution algorithm examines all of the possible15
alternatives for an expression to determine the best match. In resolving a function call expression, each16
combination of function value and list of argument alternatives is examined. Given a particular argument17
list and function value, the list of argument alternatives is flattened to produce a list of non-tuple valued18
expressions. Then the flattened list of expressions is compared with each value in the function’s parameter19
list. If the parameter’s type is not a tuple type, then the current argument value is unified with the parameter20
type, and on success the next argument and parameter are examined. If the parameter’s type is a tuple type,21
then the structuring conversion takes effect, recursively applying the parameter matching algorithm using22
the tuple’s component types as the parameter list types. Assuming a successful unification, eventually the23
algorithm gets to the end of the tuple type, which causes all of the matching expressions to be consumed24
and structured into a tuple expression. For example, in25
int f(int, [double, int]);26
f([5, 10.2], 4);27
There is only a single definition of f, and 3 arguments with only single interpretations. First, the argument28
alternative list [5, 10.2], 4 is flattened to produce the argument list 5, 10.2, 4. Next, the parameter matching29
algorithm begins, with P = int and A = int, which unifies exactly. Moving to the next parameter and argu-30
ment, P = [double, int] and A = double. This time, the parameter is a tuple type, so the algorithm applies31
recursively with P′ = double and A = double, which unifies exactly. Then P′ = int and A = double, which32
again unifies exactly. At this point, the end of P′ has been reached, so the arguments 10.2, 4 are structured33
into the tuple expression [10.2, 4]. Finally, the end of the parameter list P has also been reached, so the final34
expression is f(5, [10.2, 4]).35
19.6 Assignment36
An assignment where the left side of the assignment operator has a tuple type is called tuple assignment.37
There are two kinds of tuple assignment depending on whether the right side of the assignment operator has38
a non-tuple or tuple type, called mass and multiple assignment, respectively.39
int x;40
double y;41
[int, double] z;42
[y, x] = 3.14; // mass assignment43
[x, y] = z; // multiple assignment44
z = 10; // mass assignment45
z = [x, y]; // multiple assignment46
38 19.6 Assignment
Let Li for i in [0,n) represent each component of the flattened left side, Ri represent each component of the1
flattened right side of a multiple assignment, and R represent the right side of a mass assignment.2
For a multiple assignment to be valid, both tuples must have the same number of elements when flat-3
tened. For example, the following is invalid because the number of components on the left does not match4
the number of components on the right.5
[ int, int ] x, y, z;6
[ x, y ] = z; // multiple assignment, invalid 4 != 27
Multiple assignment assigns Ri to Li for each i. That is, ?=?(&Li, Ri) must be a well-typed expression. In the8
previous example, [x, y] = z, z is flattened into z.0, z.1, and the assignments x = z.0 and y = z.1 happen.9
A mass assignment assigns the value R to each Li. For a mass assignment to be valid, ?=?(&Li, R)10
must be a well-typed expression. These semantics differ from C cascading assignment (e.g., a=b=c) in11
that conversions are applied to R in each individual assignment, which prevents data loss from the chain12
of conversions that can happen during a cascading assignment. For example, [y, x] = 3.14 performs the13
assignments y = 3.14 and x = 3.14, which results in the value 3.14 in y and the value 3 in x. On the other14
hand, the C cascading assignment y = x = 3.14 performs the assignments x = 3.14 and y = x, which results in15
the value 3 in x, and as a result the value 3 in y as well.16
Both kinds of tuple assignment have parallel semantics, such that each value on the left side and right17
side is evaluated before any assignments occur. As a result, it is possible to swap the values in two variables18
without explicitly creating any temporary variables or calling a function.19
int x = 10, y = 20;20
[ x, y ] = [ y, x ];21
After executing this code, x has the value 20 and y has the value 10.22
In C
A
, tuple assignment is an expression where the result type is the type of the left side of the assign-23
ment, as in normal assignment. That is, a tuple assignment produces the value of the left-hand side after24
assignment. These semantics allow cascading tuple assignment to work out naturally in any context where25
a tuple is permitted. These semantics are a change from the original tuple design in K-W C [29], wherein26
tuple assignment was a statement that allows cascading assignments as a special case. Restricting tuple27
assignment to statements was an attempt to to fix what was seen as a problem with side-effects, wherein28
assignment can be used in many different locations, such as in function-call argument position. While29
permitting assignment as an expression does introduce the potential for subtle complexities, it is impossible30
to remove assignment expressions from C
A
without affecting backwards compatibility. Furthermore, there31
are situations where permitting assignment as an expression improves readability by keeping code succinct32
and reducing repetition, and complicating the definition of tuple assignment puts a greater cognitive burden33
on the user. In another language, tuple assignment as a statement could be reasonable, but it would be incon-34
sistent for tuple assignment to be the only kind of assignment that is not an expression. In addition, K-W35
C permits the compiler to optimize tuple assignment as a block copy, since it does not support user-defined36
assignment operators. This optimization could be implemented in C
A
, but it requires the compiler to verify37
that the selected assignment operator is trivial.38
The following example shows multiple, mass, and cascading assignment used in one expression39
int a, b;40
double c, d;41
[ void ] f( [ int, int ] );42
f( [ c, a ] = [ b, d ] = 1.5 ); // assignments in parameter list43
The tuple expression begins with a mass assignment of 1.5 into [b, d], which assigns 1.5 into b, which is44
truncated to 1, and 1.5 into d, producing the tuple [1, 1.5] as a result. That tuple is used as the right side of45
the multiple assignment (i.e., [c, a] = [1, 1.5]) that assigns 1 into c and 1.5 into a, which is truncated to 1,46
producing the result [1, 1]. Finally, the tuple [1, 1] is used as an expression in the call to f.47
19.7 Construction 39
19.7 Construction1
Tuple construction and destruction follow the same rules and semantics as tuple assignment, except that in2
the case where there is no right side, the default constructor or destructor is called on each component of the3
tuple. As constructors and destructors did not exist in previous versions of C
A
or in K-W C, this is a primary4
contribution of this thesis to the design of tuples.5
struct S;6
void ?{}(S *); // (1)7
void ?{}(S *, int); // (2)8
void ?{}(S * double); // (3)9
void ?{}(S *, S); // (4)10
11
[S, S] x = [3, 6.28]; // uses (2), (3), specialized constructors12
[S, S] y; // uses (1), (1), default constructor13
[S, S] z = x.0; // uses (4), (4), copy constructor14
In this example, x is initialized by the multiple constructor calls ?{}(&x.0, 3) and ?{}(&x.1, 6.28), while y is15
initialized by two default constructor calls ?{}(&y.0) and ?{}(&y.1). z is initialized by mass copy constructor16
calls ?{}(&z.0, x.0) and ?{}(&z.1, x.0). Finally, x, y, and z are destructed, i.e., the calls ∧?{}(&x.0), ∧?{}(&x.1),17
∧?{}(&y.0), ∧?{}(&y.1), ∧?{}(&z.0), and ∧?{}(&z.1).18
It is possible to define constructors and assignment functions for tuple types that provide new semantics,19
if the existing semantics do not fit the needs of an application. For example, the function void ?{}([T, U] *, S);20
can be defined to allow a tuple variable to be constructed from a value of type S.21
struct S { int x; double y; };22
void ?{}([int, double] * this, S s) {23
this >0 = s.x;24
this >1 = s.y;25
}26
Due to the structure of generated constructors, it is possible to pass a tuple to a generated constructor for a27
type with a member prefix that matches the type of the tuple. For example,28
struct S { int x; double y; int z };29
[int, double] t;30
S s = t;31
The initialization of s with t works by default because t is flattened into its components, which satisfies the32
generated field constructor ?{}(S *, int, double) to initialize the first two values.33
19.8 Member-Access Expression34
Tuples may be used to select multiple fields of a record by field name. The result is a single tuple-valued35
expression whose type is the tuple of the types of the members. For example,36
struct S { char x; int y; double z; } s;37
s.[x, y, z];38
Here, the type of s.[ x, y, z ] is [ char, int, double ]. A member tuple expression has the form e.[x, y, z]; where39
e is an expression with type T, where T supports member access expressions, and x, y, z are all members of40
T with types Tx, Ty, and Tz respectively. Then the type of e.[x, y, z] is [Tx, Ty, Tz].41
A member-access tuple may be used anywhere a tuple can be used, e.g.:42
s.[ y, z, x ] = [ 3, 3.2, 'x' ]; // equivalent to s.x = ’x’, s.y = 3, s.z = 3.243
f( s.[ y, z ] ); // equivalent to f( s.y, s.z )44
40 19.9 Casting
Note, the fields appearing in a record-field tuple may be specified in any order; also, it is unnecessary to1
specify all the fields of a struct in a multiple record-field tuple.2
Since tuple-index expressions are a form of member-access expression, it is possible to use tuple-index3
expressions in conjunction with member-access expressions to restructure a tuple (e.g., rearrange compo-4
nents, drop components, duplicate components, etc.).5
[ int, int, long, double ] x;6
void f( double, long );7
8
f( x.[ 0, 3 ] ); // f( x.0, x.3 )9
x.[ 0, 1 ] = x.[ 1, 0 ]; // [ x.0, x.1 ] = [ x.1, x.0 ]10
[ long, int, long ] y = x.[ 2, 0, 2 ];11
It is possible for a member tuple expression to contain other member access expressions, e.g.:12
struct A { double i; int j; };13
struct B { int * k; short l; };14
struct C { int x; A y; B z; } v;15
v.[ x, y.[ i, j ], z.k ];16
This expression is equivalent to [ v.x, [ v.y.i, v.y.j ], v.z.k ]. That is, the aggregate expression is effectively17
distributed across the tuple allowing simple and easy access to multiple components in an aggregate without18
repetition. It is guaranteed that the aggregate expression to the left of the . in a member tuple expression is19
evaluated exactly once. As such, it is safe to use member tuple expressions on the result of a function with20
side-effects.21
[ int, float, double ] f();22
[ double, float ] x = f().[ 2, 1 ]; // f() called once23
In K-W C, member tuple expressions are known as record field tuples [29]. Since C
A
permits these tuple-24
access expressions using structures, unions, and tuples, member tuple expression or field tuple expression is25
more appropriate.26
19.9 Casting27
In C, the cast operator is used to explicitly convert between types. In C
A
, the cast operator has a secondary28
use, which is type ascription, since it forces the expression resolution algorithm to choose the lowest cost29
conversion to the target type. That is, a cast can be used to select the type of an expression when it is30
ambiguous, as in the call to an overloaded function.31
int f(); // (1)32
double f(); // (2)33
34
f(); // ambiguous - (1),(2) both equally viable35
(int)f(); // choose (2)36
Since casting is a fundamental operation in C
A
, casts need to be given a meaningful interpretation in the37
context of tuples. Taking a look at standard C provides some guidance with respect to the way casts should38
work with tuples.39
1 int f();40
2 void g();41
342
4 (void)f(); // valid, ignore results43
5 (int)g(); // invalid, void cannot be converted to int44
645
7 struct A { int x; };46
19.10 Polymorphism 41
8 (struct A)f(); // invalid, int cannot be converted to A1
In C, line 4 is a valid cast, which calls f and discards its result. On the other hand, line 5 is invalid, because2
g does not produce a result, so requesting an int to materialize from nothing is nonsensical. Finally, line 8 is3
also invalid, because in C casts only provide conversion between scalar types [6, p. 91]. For consistency, this4
implies that any case wherein the number of components increases as a result of the cast is invalid, while5
casts that have the same or fewer number of components may be valid.6
Formally, a cast to tuple type is valid when Tn ≤ Sm, where Tn is the number of components in the target7
type and Sm is the number of components in the source type, and for each i in [0,n), Si can be cast to Ti.8
Excess elements (S j for all j in [n,m)) are evaluated, but their values are discarded so that they are not9
included in the result expression. This discarding naturally follows the way that a cast to void works in C.10
For example,11
[int, int, int] f();12
[int, [int, int], int] g();13
14
([int, double])f(); // (1) valid15
([int, int, int])g(); // (2) valid16
([void, [int, int]])g(); // (3) valid17
([int, int, int, int])g(); // (4) invalid18
([int, [int, int, int]])g(); // (5) invalid19
(1) discards the last element of the return value and converts the second element to type double. Since20
int is effectively a 1-element tuple, (2) discards the second component of the second element of the return21
value of g. If g is free of side effects, this is equivalent to [(int)(g().0), (int)(g().1.0), (int)(g().2)]. Since22
void is effectively a 0-element tuple, (3) discards the first and third return values, which is effectively23
equivalent to [(int)(g().1.0), (int)(g().1.1)]). Note that a cast is not a function call in C
A
, so flattening and24
structuring conversions do not occur for cast expressions. As such, (4) is invalid because the cast target25
type contains 4 components, while the source type contains only 3. Similarly, (5) is invalid because the cast26
([int, int, int])(g().1) is invalid. That is, it is invalid to cast [int, int] to [int, int, int].27
19.10 Polymorphism28
Due to the implicit flattening and structuring conversions involved in argument passing, otype and dtype29
parameters are restricted to matching only with non-tuple types. The integration of polymorphism, type30
assertions, and monomorphic specialization of tuple-assertions are a primary contribution of this thesis to31
the design of tuples.32
forall(otype T, dtype U)33
void f(T x, U * y);34
35
f([5, "hello"]);36
In this example, [5, "hello"] is flattened, so that the argument list appears as 5, "hello". The argument37
matching algorithm binds T to int and U to const char, and calls the function as normal.38
Tuples can contain otype and dtype components. For example, a plus operator can be written to add two39
triples of a type together.40
forall(otype T | { T ?+?(T, T); })41
[T, T, T] ?+?([T, T, T] x, [T, T, T] y) {42
return [x.0+y.0, x.1+y.1, x.2+y.2];43
}44
[int, int, int] x;45
int i1, i2, i3;46
42 19.10 Polymorphism
[i1, i2, i3] = x + ([10, 20, 30]);1
Note that due to the implicit tuple conversions, this function is not restricted to the addition of two triples. A2
call to this plus operator type checks as long as a total of 6 non-tuple arguments are passed after flattening,3
and all of the arguments have a common type that can bind to T, with a pairwise ?+? over T. For example,4
these expressions also succeed and produce the same value.5
([x.0, x.1]) + ([x.2, 10, 20, 30]); // x + ([10, 20, 30])6
x.0 + ([x.1, x.2, 10, 20, 30]); // x + ([10, 20, 30])7
This presents a potential problem if structure is important, as these three expressions look like they should8
have different meanings. Furthermore, these calls can be made ambiguous by introducing seemingly different9
functions.10
forall(otype T | { T ?+?(T, T); })11
[T, T, T] ?+?([T, T] x, [T, T, T, T]);12
forall(otype T | { T ?+?(T, T); })13
[T, T, T] ?+?(T x, [T, T, T, T, T]);14
It is also important to note that these calls could be disambiguated if the function return types were different,15
as they likely would be for a reasonable implementation of ?+?, since the return type is used in overload16
resolution. Still, these semantics are a deficiency of the current argument matching algorithm, and depending17
on the function, differing return values may not always be appropriate. These issues could be rectified by18
applying an appropriate conversion cost to the structuring and flattening conversions, which are currently19
0-cost conversions in the expression resolver. Care would be needed in this case to ensure that exact matches20
do not incur such a cost.21
void f([int, int], int, int);22
23
f([0, 0], 0, 0); // no cost24
f(0, 0, 0, 0); // cost for structuring25
f([0, 0,], [0, 0]); // cost for flattening26
f([0, 0, 0], 0); // cost for flattening and structuring27
Until this point, it has been assumed that assertion arguments must match the parameter type exactly,28
modulo polymorphic specialization (i.e., no implicit conversions are applied to assertion arguments). This29
decision presents a conflict with the flexibility of tuples.30
19.10.1 Assertion Inference31
int f([int, double], double);32
forall(otype T, otype U | { T f(T, U, U); })33
void g(T, U);34
g(5, 10.21);35
If assertion arguments must match exactly, then the call to g cannot be resolved, since the expected type36
of f is flat, while the only f in scope requires a tuple type. Since tuples are fluid, this requirement reduces37
the usability of tuples in polymorphic code. To ease this pain point, function parameter and return lists38
are flattened for the purposes of type unification, which allows the previous example to pass expression39
resolution.40
This relaxation is made possible by extending the existing thunk generation scheme, as described by41
Bilson [2]. Now, whenever a candidate’s parameter structure does not exactly match the formal parameter’s42
structure, a thunk is generated to specialize calls to the actual function.43
int thunk(int p0, double p1, double p2) {44
return f([ p0, p1], p2);45
}46
20 Tuples 43
Essentially, this provides flattening and structuring conversions to inferred functions, improving the compat-1
ibility of tuples and polymorphism.2
20 Tuples3
In C and C
A
, lists of elements appear in several contexts, such as the parameter list for a routine call. (More4
contexts are added shortly.) A list of such elements is called a lexical list. The general syntax of a lexical5
list is:6
[ exprlist ]7
where exprlist is a list of one or more expressions separated by commas. The brackets, [], allow differenti-8
ating between lexical lists and expressions containing the C comma operator. The following are examples9
of lexical lists:10
[ x, y, z ]11
[ 2 ]12
[ v+w, x*y, 3.14159, f() ]13
Tuples are permitted to contain sub-tuples (i.e., nesting), such as [ [ 14, 21 ], 9 ], which is a 2-element tuple14
whose first element is itself a tuple. Note, a tuple is not a record (structure); a record denotes a single value15
with substructure, whereas a tuple is multiple values with no substructure (see flattening coercion in Section16
12.1). In essence, tuples are largely a compile time phenomenon, having little or no runtime presence.17
Tuples can be organized into compile-time tuple variables; these variables are of tuple type. Tuple18
variables and types can be used anywhere lists of conventional variables and types can be used. The general19
syntax of a tuple type is:20
[ typelist ]21
where typelist is a list of one or more legal C
A
or C type specifications separated by commas, which may22
include other tuple type specifications. Examples of tuple types include:23
[ unsigned int, char ]24
[ double, double, double ]25
[ * int, int * ] // mix of CFA and ANSI26
[ * [ 5 ] int, * * char, * [ [ int, int ] ] (int, int) ]27
Like tuples, tuple types may be nested, such as [ [ int, int ], int ], which is a 2-element tuple type whose first28
element is itself a tuple type.29
Examples of declarations using tuple types are:30
[ int, int ] x; // 2 element tuple, each element of type int31
* [ char, char ] y; // pointer to a 2 element tuple32
[ [ int, int ] ] z ([ int, int ]);33
The last example declares an external routine that expects a 2 element tuple as an input parameter and returns34
a 2 element tuple as its result.35
As mentioned, tuples can appear in contexts requiring a list of value, such as an argument list of a36
routine call. In unambiguous situations, the tuple brackets may be omitted, e.g., a tuple that appears as an37
argument may have its square brackets omitted for convenience; therefore, the following routine invocations38
are equivalent:39
f( [ 1, x+2, fred() ] );40
f( 1, x+2, fred() );41
Also, a tuple or a tuple variable may be used to supply all or part of an argument list for a routine expecting42
multiple input parameters or for a routine expecting a tuple as an input parameter. For example, the following43
are all legal:44
[ int, int ] w1;45
44 20.1 Tuple Coercions
[ int, int, int ] w2;1
[ void ] f (int, int, int); // three input parameters of type int2
[ void ] g ([ int, int, int ]); 3 element tuple as input3
f( [ 1, 2, 3 ] );4
f( w1, 3 );5
f( 1, w1 );6
f( w2 );7
g( [ 1, 2, 3 ] );8
g( w1, 3 );9
g( 1, w1 );10
g( w2 );11
Note, in all cases 3 arguments are supplied even though the syntax may appear to supply less than 3. As12
mentioned, a tuple does not have structure like a record; a tuple is simply converted into a list of components.13
✷ The present implementation of C
A
does not support nested routine calls when the inner routine14
returns multiple values; i.e., a statement such as g( f() ) is not supported. Using a temporary variable15
to store the results of the inner routine and then passing this variable to the outer routine works,16
however. ✷17
A tuple can contain a C comma expression, provided the expression containing the comma operator is18
enclosed in parentheses. For instance, the following tuples are equivalent:19
[ 1, 3, 5 ]20
[ 1, (2, 3), 5 ]21
The second element of the second tuple is the expression (2, 3), which yields the result 3. This requirement22
is the same as for comma expressions in argument lists.23
Type qualifiers, i.e., const and volatile, may modify a tuple type. The meaning is the same as for a type24
qualifier modifying an aggregate type [Int99, x 6.5.2.3(7),x 6.7.3(11)], i.e., the qualifier is distributed across25
all of the types in the tuple, e.g.:26
const volatile [ int, float, const int ] x;27
is equivalent to:28
[ const volatile int, const volatile float, const volatile int ] x;29
Declaration qualifiers can only appear at the start of a C
A
tuple declaration4, e.g.:30
extern [ int, int ] w1;31
static [ int, int, int ] w2;32
✷ Unfortunately, C’s syntax for subscripts precluded treating them as tuples. The C subscript list33
has the form [i][j]... and not [i, j, ...]. Therefore, there is no syntactic way for a routine returning34
multiple values to specify the different subscript values, e.g., f[ g() ] always means a single subscript35
value because there is only one set of brackets. Fixing this requires a major change to C because the36
syntactic form M[i, j, k] already has a particular meaning: i, j, k is a comma expression. ✷37
20.1 Tuple Coercions38
There are four coercions that can be performed on tuples and tuple variables: closing, opening, flattening39
and structuring. In addition, the coercion of dereferencing can be performed on a tuple variable to yield its40
value(s), as for other variables. A closing coercion takes a set of values and converts it into a tuple value,41
which is a contiguous set of values, as in:42
[ int, int, int, int ] w;43
w = [ 1, 2, 3, 4 ];44
20.2 Mass Assignment 45
First the right-hand tuple is closed into a tuple value and then the tuple value is assigned.1
An opening coercion is the opposite of closing; a tuple value is converted into a tuple of values, as in:2
[ a, b, c, d ] = w3
w is implicitly opened to yield a tuple of four values, which are then assigned individually.4
A flattening coercion coerces a nested tuple, i.e., a tuple with one or more components, which are5
themselves tuples, into a flattened tuple, which is a tuple whose components are not tuples, as in:6
[ a, b, c, d ] = [ 1, [ 2, 3 ], 4 ];7
First the right-hand tuple is flattened and then the values are assigned individually. Flattening is also8
performed on tuple types. For example, the type [ int, [ int, int ], int ] can be coerced, using flattening, into9
the type [ int, int, int, int ].10
A structuring coercion is the opposite of flattening; a tuple is structured into a more complex nested11
tuple. For example, structuring the tuple [ 1, 2, 3, 4 ] into the tuple [ 1, [ 2, 3 ], 4 ] or the tuple type [ int, int, int, int ]12
into the tuple type [ int, [ int, int ], int ]. In the following example, the last assignment illustrates all the tuple13
coercions:14
[ int, int, int, int ] w = [ 1, 2, 3, 4 ];15
int x = 5;16
[ x, w ] = [ w, x ]; // all four tuple coercions17
Starting on the right-hand tuple in the last assignment statement, w is opened, producing a tuple of four18
values; therefore, the right-hand tuple is now the tuple [ [ 1, 2, 3, 4 ], 5 ]. This tuple is then flattened, yielding19
[ 1, 2, 3, 4, 5 ], which is structured into [ 1, [ 2, 3, 4, 5 ] ] to match the tuple type of the left-hand side. The20
tuple [ 2, 3, 4, 5 ] is then closed to create a tuple value. Finally, x is assigned 1 and w is assigned the tuple21
value using multiple assignment (see Section 14).22
✷ A possible additional language extension is to use the structuring coercion for tuples to initialize23
a complex record with a tuple. ✷24
20.2 Mass Assignment25
C
A
permits assignment to several variables at once using mass assignment [25]. Mass assignment has the26
following form:27
[ lvalue, ... , lvalue ] = expr ;28
The left-hand side is a tuple of lvalues, which is a list of expressions each yielding an address, i.e., any data29
object that can appear on the left-hand side of a conventional assignment statement. expr is any standard30
arithmetic expression. Clearly, the types of the entities being assigned must be type compatible with the31
value of the expression.32
Mass assignment has parallel semantics, e.g., the statement:33
[ x, y, z ] = 1.5;34
is equivalent to:35
x = 1.5; y = 1.5; z = 1.5;36
This semantics is not the same as the following in C:37
x = y = z = 1.5;38
as conversions between intermediate assignments may lose information. A more complex example is:39
[ i, y[i], z ] = a + b;40
which is equivalent to:41
t = a + b;42
46 20.4 Cascade Assignment
a1 = &i; a2 = &y[i]; a3 = &z;1
*a1 = t; *a2 = t; *a3 = t;2
The temporary t is necessary to store the value of the expression to eliminate conversion issues. The tempo-3
raries for the addresses are needed so that locations on the left-hand side do not change as the values are4
assigned. In this case, y[i] uses the previous value of i and not the new value set at the beginning of the mass5
assignment.6
20.3 Multiple Assignment7
C
A
also supports the assignment of several values at once, known as multiple assignment [25, 18]. Multiple8
assignment has the following form:9
[ lvalue, ... , lvalue ] = [ expr , ... , expr ];10
The left-hand side is a tuple of lvalues, and the right-hand side is a tuple of exprs. Each expr appearing on11
the right-hand side of a multiple assignment statement is assigned to the corresponding lvalues on the left-12
hand side of the statement using parallel semantics for each assignment. An example of multiple assignment13
is:14
[ x, y, z ] = [ 1, 2, 3 ];15
Here, the values 1, 2 and 3 are assigned, respectively, to the variables x, y and z. A more complex example16
is:17
[ i, y[ i ], z ] = [ 1, i, a + b ];18
Here, the values 1, i and a + b are assigned to the variables i, y[i] and z, respectively. Note, the parallel19
semantics of multiple assignment ensures:20
[ x, y ] = [ y, x ];21
correctly interchanges (swaps) the values stored in x and y. The following cases are errors:22
[ a, b, c ] = [ 1, 2, 3, 4 ];23
[ a, b, c ] = [ 1, 2 ];24
because the number of entities in the left-hand tuple is unequal with the right-hand tuple.25
As for all tuple contexts in C, side effects should not be used because C does not define an ordering for26
the evaluation of the elements of a tuple; both these examples produce indeterminate results:27
f( x++, x++ ); // C routine call with side effects in arguments28
[ v1, v2 ] = [ x++, x++ ]; // side effects in righthand side of multiple assignment29
20.4 Cascade Assignment30
As in C, C
A
mass and multiple assignments can be cascaded, producing cascade assignment. Cascade31
assignment has the following form:32
tuple = tuple = ... = tuple;33
and it has the same parallel semantics as for mass and multiple assignment. Some examples of cascade34
assignment are:35
x1 = y1 = x2 = y2 = 0;36
[ x1, y1 ] = [ x2, y2 ] = [ x3, y3 ];37
[ x1, y1 ] = [ x2, y2 ] = 0;38
[ x1, y1 ] = z = 0;39
As in C, the rightmost assignment is performed first, i.e., assignment parses right to left.40
21 Stream I/O Library 47
21 Stream I/O Library1
The goal of C
A
stream input/output (I/O) is to simplify the common cases, while fully supporting poly-2
morphism and user defined types in a consistent way. Stream I/O can be implicitly or explicitly formatted.3
Implicit formatting means C
A
selects the output or input format for values that match with the type of a4
variable. Explicit formatting means additional information is specified to augment how an output or input5
of value is interpreted. C
A
formatting is a cross between C printf and C++ cout manipulators, and Python6
implicit spacing and newline. Specifically:7
• printf/Python format codes are dense, making them difficult to read and remember. C
A
/C++ format8
manipulators are named, making them easier to read and remember.9
• printf/Python separates format codes from associated variables, making it difficult to match codes with10
variables. C
A
/C++ co-locate codes with associated variables, where C
A
has the tighter binding.11
• Format manipulators in C
A
have local effect, whereas C++ have global effect, except setw. Hence, it is12
common programming practice to toggle manipulators on and then back to the default to prevent down-13
stream side-effects. Without this programming style, errors occur when moving prints, as manipulator14
effects incorrectly flow into the new location. (To guarantee no side-effects, manipulator values must be15
saved and restored across function calls.)16
• C
A
has more sophisticated implicit spacing between values than Python, plus implicit newline at the end17
of a print.18
The C
A
header file for the I/O library is fstream.hfa.19
For implicit formatted output, the common case is printing a series of variables separated by whitespace.20
CA
C++ Python
int x = 1, y = 2, z = 3;sout | x | y | z; cout << x << " " << y << " " << z << endl;
x = 1; y = 2; z = 3print( x, y, z )
1 2 3 1 2 3 1 2 3
21
The C
A
form has half the characters of the C++ form, and is similar to Python I/O with respect to implicit22
separators and newline. Similar simplification occurs for tuple I/O, which flattens the tuple and prints each23
value separated by “, ”.24
[int, [ int, int ] ] t1 = [ 1, [ 2, 3 ] ], t2 = [ 4, [ 5, 6 ] ];25
sout | t1 | t2; // print tuples26
1, 2, 3 4, 5, 627
Finally, C
A
uses the logical-or operator for I/O as it is the lowest-priority overloadable operator, other than28
assignment. Therefore, fewer output expressions require parenthesis.29
C
A
: sout | x * 3 | y + 1 | z << 2 | x == y | (x | y) | (x || y) | (x > z ? 1 : 2);
C++: cout << x * 3 << y + 1 << (z << 2) << (x == y) << (x | y) << (x || y) << (x > z ? 1 : 2) << endl;
3 3 12 0 3 1 2
30
Input and output use a uniform operator, |, rather than separate operators, as in >> and << for C++. There is a31
weak similarity between the C
A
logical-or operator and the Shell pipe-operator for moving data, where data32
flows in the correct direction for input but the opposite direction for output.33
For implicit formatted input, the common case is reading a sequence of values separated by whitespace,34
where the type of an input constant must match with the type of the input variable.35
48 21.2 Separation Manipulators
int x; double y char z;
C
A
C++ Python
sin | x | y | z; cin >> x >> y >> z; x = int(input()); y = float(input()); z = input();
1 2.5 A 1 2.5 A 12.5
A
1
21.1 Implicit Separator2
The implicit separator character (space/blank) is a separator not a terminator for output. The rules for3
implicitly adding the separator are:4
1. A separator does not appear at the start or end of a line.5
sout | 1 | 2 | 3;6
1 2 37
2. A separator does not appear before or after a character literal or variable.8
sout | '1' | '2' | '3';9
12310
3. A separator does not appear before or after a null (empty) C string, which is a local mechanism to11
disable insertion of the separator character.12
sout | 1 | "" | 2 | "" | 3;13
12314
4. A separator does not appear before a C string starting with the (extended) ASCII characters: ,.;!?)]}%¢»,15
where » is a closing citation mark.16
sout | 1 | ", x" | 2 | ". x" | 3 | "; x" | 4 | "! x" | 5 | "? x" | 6 | "% x"17
| 7 | "¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x";18
1, x 2. x 3; x 4! x 5? x 6% x 7¢ x 8» x 9) x 10] x 11} x19
5. A separator does not appear after a C string ending with the (extended) ASCII characters: ([{=$£¥¡¿«,20
where ¡¿ are inverted opening exclamation and question marks, and « is an opening citation mark.21
sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x ¥"22
| 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;23
x (1 x [2 x {3 x =4 x $5 x £6 x ¥7 x ¡8 x ¿9 x «1024
6. A seperator does not appear before/after a C string starting/ending with the ASCII quote or whitespace25
characters: ‘'": \t\v\f\r\n26
sout | "x‘" | 1 | "‘x'" | 2 | "'x\"" | 3 | "\"x:" | 4 | ":x " | 5 | " x\t" | 6 | "\tx";27
x‘1‘x’2’x"3"x:4:x 5 x 6 x28
7. If a space is desired before or after one of the special string start/end characters, simply insert a space.29
sout | "x ( " | 1 | " ) x" | 2 | " , x" | 3 | " :x: " | 4;30
x ( 1 ) x 2 , x 3 :x: 431
21.2 Separation Manipulators32
The following manipulators control implicit output separation. The effect of these manipulators is global33
for an output stream (except sepOn and sepOff).34
21.2 Separation Manipulators 49
1. sepSet and sep/sepGet set and get the separator string. The separator string can be at most 16 char-1
acters including the '\0' string terminator (15 printable characters).2
sepSet( sout, ", $" ); // set separator from " " to ", $"3
sout | 1 | 2 | 3 | " \"" | sep | "\"";4
1, $2, $3 ", $"5
sepSet( sout, " " ); // reset separator to " "6
sout | 1 | 2 | 3 | " \"" | sepGet( sout ) | "\"";7
1 2 3 " "8
sepGet can be used to store a separator and then restore it:9
char store[sepSize]; // sepSize is the maximum separator size10
strcpy( store, sepGet( sout ) ); // copy current separator11
sepSet( sout, "_" ); // change separator to underscore12
sout | 1 | 2 | 3;13
1 2 314
sepSet( sout, store ); // change separator back to original15
sout | 1 | 2 | 3;16
1 2 317
2. sepSetTuple and sepTuple/sepGetTuple get and set the tuple separator-string. The tuple separator-18
string can be at most 16 characters including the '\0' string terminator (15 printable characters).19
sepSetTuple( sout, " " ); // set tuple separator from ", " to " "20
sout | t1 | t2 | " \"" | sepTuple | "\"";21
1 2 3 4 5 6 " "22
sepSetTuple( sout, ", " ); // reset tuple separator to ", "23
sout | t1 | t2 | " \"" | sepGetTuple( sout ) | "\"";24
1, 2, 3 4, 5, 6 ", "25
As for sepGet, sepGetTuple can be use to store a tuple separator and then restore it.26
3. sepDisable and sepEnable toggle printing the separator.27
sout | sepDisable | 1 | 2 | 3; // turn off implicit separator28
12329
sout | sepEnable | 1 | 2 | 3; // turn on implicit separator30
1 2 331
4. sepOn and sepOff toggle printing the separator with respect to the next printed item, and then return32
to the global seperator setting.33
sout | 1 | sepOff | 2 | 3; // turn off implicit separator for the next item34
12 335
sout | sepDisable | 1 | sepOn | 2 | 3; // turn on implicit separator for the next item36
1 2337
The tuple separator also responses to being turned on and off.38
sout | t1 | sepOff | t2; // turn off implicit separator for the next item39
1, 2, 34, 5, 640
sepOn cannot be used to start/end a line with a separator because separators do not appear at the41
start/end of a line; use sep to accomplish this functionality.42
sout | sepOn | 1 | 2 | 3 | sepOn; // sepOn does nothing at start/end of line43
50 21.4 Output Value Manipulators
1 2 31
sout | sep | 1 | 2 | 3 | sep ; // use sep to print separator at start/end of line2
1 2 33
21.3 Newline Manipulators4
The following manipulators control newline separation for input and output.5
For input:6
1. nl scans characters until the next newline character, i.e., ignore the remaining characters in the line.7
2. nlOn reads the newline character, when reading single characters.8
3. nlOff does not read the newline character, when reading single characters.9
For example, in:10
sin | i | nl | j;11
1 212
313
variable i is assigned 1, the 2 is skipped, and variable j is assigned 3.14
For output:15
1. nl inserts a newline.16
sout | nl; // only print newline17
sout | 2; // implicit newline18
sout | 3 | nl | 4 | nl; // terminating nl merged with implicit newline19
sout | 5 | nl | nl; // again terminating nl merged with implicit newline20
sout | 6; // implicit newline21
22
223
324
425
526
27
628
Note, a terminating nl is merged (overrides) with the implicit newline at the end of the sout expression,29
otherwise it is impossible to to print a single newline30
2. nlOn implicitly prints a newline at the end of each output expression.31
3. nlOff does not implicitly print a newline at the end of each output expression.32
21.4 Output Value Manipulators33
The following manipulators control formatting of output values (printing), and only affect the format of the34
argument.35
1. bin( integer ) print value in base 2 preceded by 0b/0B.36
sout | bin( 0 ) | bin( 27HH ) | bin( 27H ) | bin( 27 ) | bin( 27L );37
0b0 0b11011 0b11011 0b11011 0b1101138
sout | bin( 27HH ) | bin( 27H ) | bin( 27 ) | bin( 27L );39
0b11100101 0b1111111111100101 0b11111111111111111111111111100101 0b(58 1s)10010140
21.4 Output Value Manipulators 51
2. oct( integer ) print value in base 8 preceded by 0.1
sout | oct( 0 ) | oct( 27HH ) | oct( 27H ) | oct( 27 ) | oct( 27L );2
0 033 033 033 0333
sout | oct( 27HH ) | oct( 27H ) | oct( 27 ) | oct( 27L );4
0345 0177745 037777777745 017777777777777777777455
Note, octal 0 is not preceded by 0 to prevent confusion.6
3. hex( integer / floating-point ) print value in base 16 preceded by 0x/0X.7
sout | hex( 0 ) | hex( 27HH ) | hex( 27H ) | hex( 27 ) | hex( 27L );8
0 0x1b 0x1b 0x1b 0x1b9
sout | hex( 27HH ) | hex( 27H ) | hex( 27 ) | hex( 27L );10
0xe5 0xffe5 0xffffffe5 0xffffffffffffffe511
12
sout | hex( 0.0 ) | hex( 27.5F ) | hex( 27.5 ) | hex( 27.5L );13
0x0.p+0 0x1.b8p+4 0x1.b8p+4 0xd.cp+114
sout | hex( 27.5F ) | hex( 27.5 ) | hex( 27.5L );15
0x1.b8p+4 0x1.b8p+4 0xd.cp+116
4. sci( floating-point ) print value in scientific notation with exponent. Default is 6 digits of precision.17
sout | sci( 0.0 ) | sci( 27.5 ) | sci( 27.5 );18
0.000000e+00 2.750000e+01 2.750000e+0119
5. upcase( bin / hex / floating-point ) print letters in a value in upper case. Lower case is the default.20
sout | upcase( bin( 27 ) ) | upcase( hex( 27 ) ) | upcase( 27.5e 10 ) | upcase( hex( 27.5 ) );21
0B11011 0X1B 2.75E 09 0X1.B8P+422
6. nobase( integer ) do not precede bin, oct, hex with 0b/0B, 0, or 0x/0X. Printing the base is the default.23
sout | nobase( bin( 27 ) ) | nobase( oct( 27 ) ) | nobase( hex( 27 ) );24
11011 33 1b25
7. nodp( floating-point ) do not print a decimal point if there are no fractional digits. Printing a decimal26
point is the default, if there are no fractional digits.27
sout | 0. | nodp( 0. ) | 27.0 | nodp( 27.0 ) | nodp( 27.5 );28
0.0 0 27.0 27 27.529
8. sign( integer / floating-point ) prefix with plus or minus sign (+ or ). Only printing the minus sign is30
the default.31
sout | sign( 27 ) | sign( 27 ) | sign( 27. ) | sign( 27. ) | sign( 27.5 ) | sign( 27.5 );32
+27 27 +27.0 27.0 +27.5 27.533
9. wd( unsigned char minimum, T val ), wd( unsigned char minimum, unsigned char precision, T val )34
For all types, minimum is the minimum number of printed characters. If the value is shorter than the35
minimum, it is padded on the right with spaces.36
sout | wd( 4, 34) | wd( 3, 34 ) | wd( 2, 34 );37
sout | wd( 10, 4.) | wd( 9, 4. ) | wd( 8, 4. );38
sout | wd( 4, "ab" ) | wd( 3, "ab" ) | wd( 2, "ab" );39
34 34 3440
4.000000 4.000000 4.00000041
ab ab ab42
If the value is larger, it is printed without truncation, ignoring the minimum.43
52 21.4 Output Value Manipulators
sout | wd( 4, 34567 ) | wd( 3, 34567 ) | wd( 2, 34567 );1
sout | wd( 4, 3456. ) | wd( 3, 3456. ) | wd( 2, 3456. );2
sout | wd( 4, "abcde" ) | wd( 3, "abcde" ) | wd( 2,"abcde" );3
34567 34567 345674
3456. 3456. 3456.5
abcde abcde abcde6
For integer types, precision is the minimum number of printed digits. If the value is shorter, it is7
padded on the left with leading zeros.8
sout | wd( 4,3, 34 ) | wd( 8,4, 34 ) | wd( 10,10, 34 );9
034 0034 000000003410
If the value is larger, it is printed without truncation, ignoring the precision.11
sout | wd( 4,1, 3456 ) | wd( 8,2, 3456 ) | wd( 10,3, 3456 );12
3456 3456 345613
If precision is 0, nothing is printed for zero. If precision is greater than the minimum, it becomes the14
minimum.15
sout | wd( 4,0, 0 ) | wd( 3,10, 34 );16
000000003417
For floating-point types, precision is the minimum number of digits after the decimal point.18
sout | wd( 6,3, 27.5 ) | wd( 8,1, 27.5 ) | wd( 8,0, 27.5 ) | wd( 3,8, 27.5 );19
27.500 27.5 28. 27.5000000020
For the C-string type, precision is the maximum number of printed characters, so the string is truncared21
if it exceeds the maximum.22
sout | wd( 6,8, "abcd" ) | wd( 6,8, "abcdefghijk" ) | wd( 6,3, "abcd" );23
abcd abcdefgh abc24
10. ws( unsigned char minimum, unsigned char significant, floating point ) For floating-point type, minimum25
is the same as for manipulator wd, but significant is the maximum number of significant digits to be26
printed for both the integer and fractions (versus only the fraction for wd). If a value’s significant27
digits is greater than significant, the last significant digit is rounded up.28
sout | ws(6,6, 234.567) | ws(6,5, 234.567) | ws(6,4, 234.567) | ws(6,3, 234.567);29
234.567 234.57 234.6 23530
If a value’s magnitude is greater than significant, the value is printed in scientific notation with the31
specified number of significant digits.32
sout | ws(6,6, 234567.) | ws(6,5, 234567.) | ws(6,4, 234567.) | ws(6,3, 234567.);33
234567. 2.3457e+05 2.346e+05 2.35e+0534
If significant is greater than minimum, it defines the number of printed characters.35
sout | ws(3,6, 234567.) | ws(4,6, 234567.) | ws(5,6, 234567.) | ws(6,6, 234567.);36
234567. 234567. 234567. 234567.37
11. left( field-width ) left justify within the given field.38
sout | left(wd(4, 27)) | left(wd(10, 27.)) | left(wd(10, 27.5)) | left(wd(4,3, 27)) | left(wd(10,3, 27.5));39
27 27.000000 27.500000 027 27.50040
12. pad0( field-width ) left pad with zeroes (0).41
sout | pad0( wd( 4, 27 ) ) | pad0( wd( 4,3, 27 ) ) | pad0( wd( 8,3, 27.5 ) );42
0027 027 0027.50043
21.5 Input Value Manipulators 53
21.5 Input Value Manipulators1
The format of numeric input values in the same as C constants without a trailing type suffix, as the input2
value-type is denoted by the input variable. For Bool type, the constants are true and false. For integral3
types, any number of digits, optionally preceded by a sign (+ or ), where a4
• 1-9 prefix introduces a decimal value (0-9),5
• 0 prefix introduces an octal value (0-7), and6
• 0x or 0X prefix introduces a hexadecimal value (0-f) with lower or upper case letters.7
For floating-point types, any number of decimal digits, optionally preceded by a sign (+ or ), optionally8
containing a decimal point, and optionally followed by an exponent, e or E, with signed (optional) decimal9
digits. Floating-point values can also be written in hexadecimal format preceded by 0x or 0X with hexadec-10
imal digits and exponent denoted by p or P.11
For the C-string type, the input values are not the same as C-string constants, i.e., double quotes brack-12
eting arbitrary text with escape sequences. Instead, the next sequence of non-whitespace characters are13
read, and the input sequence is terminated with delimiter '\0'. The string variable must be large enough to14
contain the input sequence.15
The following manipulators control formatting of input values (reading), and only affect the format of16
the argument.17
1. skip( const char * pattern ) / skip( unsigned int length ) / const char * pattern The argument defines18
a pattern or length. The pattern is composed of white-space and non-white-space characters, where19
any white-space character matches 0 or more input white-space characters (hence, consecutive white-20
space characters in the pattern are combined), and each non-white-space character matches exactly21
with an input character. The length is composed of the next N characters, including the newline22
character. If the match successes, the input characters are discarded, and input continues with the next23
character. If the match fails, the input characters are left unread.24
char sk[ ] = "abc";25
sin | "abc " | skip( sk ) | skip( 5 ); // match input sequence26
abc27
abc28
xx29
2. wdi( unsigned int maximum, T & val ) For all types except char, maximum is the maximum number30
of characters read for the current operation.31
char s[10]; int i; double d;32
sin | wdi( 4, s ) | wdi( 3, i ) | wdi( 8, d ); // c == "abcd", i == 123, d == 3.456E+233
abcd1233.456E+234
Note, input wdi cannot be overloaded with output wd because both have the same parameters but35
return different types. Currently, C
A
cannot distinguish between these two manipulators in the middle36
of an sout/sin expression based on return type.37
3. ignore( T & val ) For all types, the data is read from the stream depending on the argument type but38
ignored, i.e., it is not stored in the argument.39
double d;40
sin | ignore( d ); // d is unchanged41
75.35e 4 2542
4. incl( const char * scanset, char * s ) For the C-string type, the argument defines a scanset that matches43
any number of characters in the set. Matching characters are read into the C string and null terminated.44
54 22.1 Type Definitions
char s[10];1
sin | incl( "abc", s );2
bcaxyz3
5. excl( const char * scanset, char * s ) For the C-string type, the argument defines a scanset that matches4
any number of characters not in the set. Non-matching characters are read into the C string and null5
terminated.6
char s[10];7
sin | excl( "abc", s );8
xyzbca9
22 Types10
22.1 Type Definitions11
C
A
allows users to define new types using the keyword type.12
// SensorValue is a distinct type and represented as an int13
type SensorValue = int;14
A type definition is different from a typedef in C because a typedef just creates an alias for a type, while15
Do.s type definition creates a distinct type. This means that users can define distinct function overloads for16
the new type (see Overloading for more information). For example:17
type SensorValue = int;18
void printValue(int v) {...}19
void printValue(SensorValue v) {...}20
void process(int v) {...}21
22
SensorValue s = ...;23
24
printValue(s); // calls version with SensorValue argument25
26
printValue((int) s); // calls version with int argument27
28
process(s); // implicit conversion to int29
If SensorValue was defined with a typedef, then these two print functions would not have unique signa-30
tures. This can be very useful to create a distinct type that has the same representation as another type.31
The compiler will assume it can safely convert from the old type to the new type, implicitly. Users may32
override this and define a function that must be called to convert from one type to another.33
type SensorValue = int;34
// ()? is the overloaded conversion operator identifier35
// This function converts an int to a SensorValue36
SensorValue ()?(int val) {37
...38
}39
void process(int v) {...}40
41
SensorValue s = ...;42
process(s); // implicit call to conversion operator43
In many cases, it is not desired for the compiler to do this implicit conversion. To avoid that, the user44
can use the explicit modifier on the conversion operator. Any places where the conversion is needed but not45
explicit (with a cast), will result in a compile-time error.46
22.2 Structures 55
type SensorValue = int;1
2
// conversion from int to SensorValue; must be explicit3
explicit SensorValue ()?(int val) {4
...5
}6
7
void process(int v) {...}8
9
SensorValue s = ...;10
process(s); // implicit cast to int: compile time error11
process((int) s); // explicit cast to int: calls conversion func12
The conversion may not require any code, but still need to be explicit; in that case, the syntax can be13
simplified to:14
type SensorValue = int;15
explicit SensorValue ()?(int);16
void process(int v) {...}17
18
SensorValue s = ...;19
process(s); // compile time error20
process((int) s); // type is converted, no function is called21
22.2 Structures22
Structures in C
A
are basically the same as structures in C. A structure is defined with the same syntax as in23
C. When referring to a structure in C
A
, users may omit the struct keyword.24
struct Point {25
double x;26
double y;27
};28
29
Point p = {0.0, 0.0};30
C
A
does not support inheritance among types, but instead uses composition to enable reuse of structure31
fields. Composition is achieved by embedding one type into another. When type A is embedded in type B,32
an object with type B may be used as an object of type A, and the fields of type A are directly accessible.33
Embedding types is achieved using anonymous members. For example, using Point from above:34
void foo(Point p);35
36
struct ColoredPoint {37
Point; // anonymous member (no identifier)38
int Color;39
};40
...41
ColoredPoint cp = ...;42
cp.x = 10.3; // x from Point is accessed directly43
cp.color = 0x33aaff; // color is accessed normally44
foo(cp); // cp can be used directly as a Point45
56 24.1 Overloaded Constant
23 Constructors and Destructors1
C
A
supports C initialization of structures, but it also adds constructors for more advanced initialization.2
Additionally, C
A
adds destructors that are called when a variable is deallocated (variable goes out of scope3
or object is deleted). These functions take a reference to the structure as a parameter (see References for4
more information).5
24 Overloading6
Overloading refers to the capability of a programmer to define and use multiple objects in a program with the7
same name. In C
A
, a declaration may overload declarations from outer scopes with the same name, instead8
of hiding them as is the case in C. This may cause identical C and C
A
programs to behave differently. The9
compiler selects the appropriate object (overload resolution) based on context information at the place where10
it is used. Overloading allows programmers to give functions with different signatures but similar semantics11
the same name, simplifying the interface to users. Disadvantages of overloading are that it can be used to12
give functions with different semantics the same name, causing confusion, or that the compiler may resolve13
to a different function from what the programmer expected. C
A
allows overloading of functions, operators,14
variables, and even the constants 0 and 1.15
The compiler follows some overload resolution rules to determine the best interpretation of all of these16
overloads. The best valid interpretations are the valid interpretations that use the fewest unsafe conversions.17
Of these, the best are those where the functions and objects involved are the least polymorphic. Of these, the18
best have the lowest total conversion cost, including all implicit conversions in the argument expressions.19
Of these, the best have the highest total conversion cost for the implicit conversions (if any) applied to the20
argument expressions. If there is no single best valid interpretation, or if the best valid interpretation is21
ambiguous, then the resulting interpretation is ambiguous. For details about type inference and overload22
resolution, please see the CA
Language Specification.23
int foo(int a, int b) {24
float sum = 0.0;25
float special = 1.0;26
{27
int sum = 0;28
// both the float and int versions of sum are available29
float special = 4.0;30
// this inner special hides the outer version31
...32
}33
...34
}35
24.1 Overloaded Constant36
The constants 0 and 1 have special meaning. In C
A
, as in C, all scalar types can be incremented and37
decremented, which is defined in terms of adding or subtracting 1. The operations &&, ||, and ! can be38
applied to any scalar arguments and are defined in terms of comparison against 0 (ex. (a && b) becomes39
(a != 0 && b != 0)).40
In C, the integer constants 0 and 1 suffice because the integer promotion rules can convert them to41
any arithmetic type, and the rules for pointer expressions treat constant expressions evaluating to 0 as a42
special case. However, user-defined arithmetic types often need the equivalent of a 1 or 0 for their functions43
or operators, polymorphic functions often need 0 and 1 constants of a type matching their polymorphic44
24.1 Overloaded Constant 57
struct Widget {int id;
float size;Parts * optionalParts;
};
// ?{} is the constructor operator identifier
// The first argument is a reference to the type to initialize// Subsequent arguments can be specified for initialization
void ?{}(Widget &w) { // default constructorw.id = 1;
w.size = 0.0;
w.optionalParts = 0;}
// constructor with values (does not need to include all fields)
void ?{}(Widget &w, int id, float size) {
w.id = id;w.size = size;
w.optionalParts = 0;
}
// ∧? is the destructor operator identifiervoid ∧?(Widget &w) { // destructor
w.id = 0;
w.size = 0.0;if (w.optionalParts != 0) {
// This is the only pointer to optionalParts, free itfree(w.optionalParts);
w.optionalParts = 0;
}}
Widget baz; // reserve space onlyWidget foo{}; // calls default constructor
Widget bar{23, 2.45}; // calls constructor with valuesbaz{24, 0.91}; // calls constructor with values
?{}(baz, 24, 0.91}; // explicit call to constructor∧bar; // explicit call to destructor∧?(bar); // explicit call to destructor
Figure 5: Constructors and Destructors
58 24.3 Function Overloading
parameters, and user-defined pointer-like types may need a null value. Defining special constants for a1
user-defined type is more efficient than defining a conversion to the type from Bool.2
Why just 0 and 1? Why not other integers? No other integers have special status in C. A facility3
that let programmers declare specific constants..const Rational 12., for instance. would not be much of an4
improvement. Some facility for defining the creation of values of programmer-defined types from arbitrary5
integer tokens would be needed. The complexity of such a feature does not seem worth the gain.6
For example, to define the constants for a complex type, the programmer would define the following:7
struct Complex {8
double real;9
double imaginary;10
}11
12
const Complex 0 = {0, 0};13
const Complex 1 = {1, 0};14
...15
16
Complex a = 0;17
...18
19
a++;20
...21
if (a) { // same as if (a == 0)22
...23
}24
24.2 Variable Overloading25
The overload rules of C
A
allow a programmer to define multiple variables with the same name, but different26
types. Allowing overloading of variable names enables programmers to use the same name across multiple27
types, simplifying naming conventions and is compatible with the other overloading that is allowed. For28
example, a developer may want to do the following:29
int pi = 3;30
float pi = 3.14;31
char pi = .p.;32
24.3 Function Overloading33
Overloaded functions in C
A
are resolved based on the number and type of arguments, type of return value,34
and the level of specialization required (specialized functions are preferred over generic).35
The examples below give some basic intuition about how the resolution works.36
// Choose the one with less conversions37
int doSomething(int value) {...} // option 138
int doSomething(short value) {...} // option 239
40
int a, b = 4;41
short c = 2;42
43
a = doSomething(b); // chooses option 144
a = doSomething(c); // chooses option 245
46
24.4 Operator Overloading 59
// Choose the specialized version over the generic1
2
generic(type T)3
T bar(T rhs, T lhs) {...} // option 34
float bar(float rhs, float lhs){...} // option 45
float a, b, c;6
double d, e, f;7
c = bar(a, b); // chooses option 48
9
// specialization is preferred over unsafe conversions10
11
f = bar(d, e); // chooses option 512
24.4 Operator Overloading13
C
A
also allows operators to be overloaded, to simplify the use of user-defined types. Overloading the opera-14
tors allows the users to use the same syntax for their custom types that they use for built-in types, increasing15
readability and improving productivity. C
A
uses the following special identifiers to name overloaded opera-16
tors:17
?[?] subscripting
?() function call
?++ postfix increment
? postfix decrement
++? prefix increment
? prefix decrement
*? dereference
+? unary plus
? arithmetic negation∼? bitwise negation
!? logical complement
?\? exponentiation
?*? multiplication
?/? division
?%? remainder
?+? addition
? ? subtraction
?<<? left shift
?>>? right shift
?<? less than
?<=? less than or equal
?>=? greater than or equal
?>? greater than
?==? equality
?!=? inequality
?&? bitwise AND
?∧? exclusive OR
?|? inclusive OR
?=? simple assignment
?\=? exponentiation assignment
?*=? multiplication assignment
?/=? division assignment
?%=? remainder assignment
?+=? addition assignment
? =? subtraction assignment
?<<=? left-shift assignment
?>>=? right-shift assignment
?&=? bitwise AND assignment
?∧=? exclusive OR assignment
?|=? inclusive OR assignment
Table 1: Operator Identifiers
These identifiers are defined such that the question marks in the name identify the location of the18
operands. These operands represent the parameters to the functions, and define how the operands are mapped19
to the function call. For example, a + b becomes ?+?(a, b).20
In the example below, a new type, myComplex, is defined with an overloaded constructor, + operator,21
and string operator. These operators are called using the normal C syntax.22
type Complex = struct { // define a Complex type23
double real;24
double imag;25
}26
27
// Constructor with default values28
29
60 25 Auto Type-Inferencing
void ?{}(Complex &c, double real = 0.0, double imag = 0.0) {1
c.real = real;2
c.imag = imag;3
}4
5
Complex ?+?(Complex lhs, Complex rhs) {6
Complex sum;7
sum.real = lhs.real + rhs.real;8
sum.imag = lhs.imag + rhs.imag;9
return sum;10
}11
12
String ()?(const Complex c) {13
// use the string conversions for the structure members14
return (String)c.real + . + . + (String)c.imag + .i.;15
}16
...17
18
Complex a, b, c = {1.0}; // constructor for c w/ default imag19
...20
c = a + b;21
print(.sum = . + c);22
25 Auto Type-Inferencing23
Auto type-inferencing occurs in a declaration where a variable’s type is inferred from its initialization ex-24
pression type.25
C++ gcc
auto j = 3.0 * 4;
int i;auto k = i;
#define expr 3.0 * i
typeof(expr) j = expr;
int i;typeof(i) k = i;
// use type of initialization expression
// use type of primary variable
26
The two important capabilities are:27
• not determining or writing long generic types,28
• ensuring secondary variables, related to a primary variable, always have the same type.29
In C
A
, typedef provides a mechanism to alias long type names with short ones, both globally and30
locally, but not eliminate the use of the short name. gcc provides typeof to declare a secondary variable31
from a primary variable. C
A
also relies heavily on the specification of the left-hand side of assignment for32
type inferencing, so in many cases it is crucial to specify the type of the left-hand side to select the correct33
type of the right-hand expression. Only for overloaded routines with the same return type is variable type-34
inferencing possible. Finally, auto presents the programming problem of tracking down a type when the35
type is actually needed. For example, given36
auto j = ...37
and the need to write a routine to compute using j38
void rtn( ... parm );39
rtn( j );40
A programmer must work backwards to determine the type of j’s initialization expression, reconstructing41
the possibly long generic type-name. In this situation, having the type name or a short alias is very useful.42
26 Concurrency 61
There is also the conundrum in type inferencing of when to brand a type. That is, when is the type of1
the variable more important than the type of its initialization expression. For example, if a change is made2
in an initialization expression, it can cause cascading type changes and/or errors. At some point, a variable3
type needs to remain constant and the expression to be in error when it changes.4
Given typedef and typeof in C
A
, and the strong need to use the type of left-hand side in inferencing, auto5
type-inferencing is not supported at this time. Should a significant need arise, this feature can be revisited.6
26 Concurrency7
Concurrency support in C
A
is implemented on top of a highly efficient runtime system of light-weight, M:N,8
user level threads. The model integrates concurrency features into the language by making the structure type9
the core unit of concurrency. All communication occurs through method calls, where data is sent via method10
arguments, and received via the return value. This enables a very familiar interface to all programmers, even11
those with no parallel programming experience. It also allows the compiler to do static type checking of all12
communication, a very important safety feature. This controlled communication with type safety has some13
similarities with channels in Go, and can actually implement channels exactly, as well as create additional14
communication patterns that channels cannot. Mutex objects, monitors, are used to contain mutual exclusion15
within an object and synchronization across concurrent threads.16
26.1 Coroutine17
Coroutines are the precursor to tasks. Figure 6 shows a coroutine that computes the Fibonacci numbers.18
26.2 Monitors19
A monitor is a structure in C
A
which includes implicit locking of its fields. Users of a monitor interact with20
it just like any structure, but the compiler handles code as needed to ensure mutual exclusion. An example21
of the definition of a monitor is shown here:22
type Account = monitor {23
const unsigned long number; // account number24
float balance; // account balance25
};26
26.3 Tasks27
C
A
also provides a simple mechanism for creating and utilizing user level threads. A task provides mutual28
exclusion like a monitor, and also has its own execution state and a thread of control. Similar to a monitor,29
a task is defined like a structure:30
27 Language Comparisons31
C
A
is one of many languages that attempts to improve upon C. In developing C
A
, many other languages32
were consulted for ideas, constructs, and syntax. Therefore, it is important to show how these languages33
each compare with Do. In this section, C
A
is compared with what the writers of this document consider to34
be the closest competitors of Do: C++, Go, Rust, and D.35
62 27.1 C++
#include <fstream>
#include <coroutine>
coroutine Fibonacci {
int fn; // used for communication};
void ?{}( Fibonacci * this ) {
this >fn = 0;}
void main( Fibonacci * this ) {
int fn1, fn2; // retained between resumesthis >fn = 0; // case 0
fn1 = this >fn;suspend(); // return to last resume
this >fn = 1; // case 1fn2 = fn1;
fn1 = this >fn;suspend(); // return to last resume
for ( ;; ) { // general casethis >fn = fn1 + fn2;
fn2 = fn1;
fn1 = this >fn;suspend(); // return to last resume
} // for}
int next( Fibonacci * this ) {
resume( this ); // transfer to last suspendreturn this >fn;
}
int main() {Fibonacci f1, f2;
for ( int i = 1; i <= 10; i += 1 ) {sout | next( &f1 ) | ' ' | next( &f2 );
} // for
}
Figure 6: Fibonacci Coroutine
27.1 C++1
C++ is a general-purpose programming language. It has imperative, object-oriented and generic program-2
ming features, while also providing facilities for low-level memory manipulation. (Wikipedia)3
The primary focus of C++ seems to be adding object-oriented programming to C, and this is the primary4
difference between C++ and Do. C++ uses classes to encapsulate data and the functions that operate on that5
data, and to hide the internal representation of the data. C
A
uses modules instead to perform these same tasks.6
Classes in C++ also enable inheritance among types. Instead of inheritance, C
A
embraces composition and7
interfaces to achieve the same goals with more flexibility. There are many studies and articles comparing8
inheritance and composition (or is-a versus has-a relationships), so we will not go into more detail here9
(Venners, 1998) (Pike, Go at Google: Language Design in the Service of Software Engineering , 2012).10
27.1 C++ 63
#include <fstream>
#include <kernel>#include <monitor>
#include <thread>
monitor global t {
int value;
};
void ?{}(global t * this) {this >value = 0;
}
static global t global;
void increment3( global t * mutex this ) {
this >value += 1;
}void increment2( global t * mutex this ) {
increment3( this );
}void increment( global t * mutex this ) {
increment2( this );}
thread MyThread {};
void main( MyThread* this ) {
for(int i = 0; i < 1 000 000; i++) {increment( &global );
}}
int main(int argc, char* argv[]) {
processor p;{
MyThread f[4];}
sout | global.value;
}
Figure 7: Atomic-Counter Monitor
Figure 8: f:AtomicCounterMonitor
64 27.1 C++
#include <fstream>
#include <kernel>
#include <stdlib>#include <thread>
thread First { signal once * lock; };
thread Second { signal once * lock; };
void ?{}( First * this, signal once* lock ) { this >lock = lock; }
void ?{}( Second * this, signal once* lock ) { this >lock = lock; }
void main( First * this ) {
for ( int i = 0; i < 10; i += 1 ) {sout | "First : Suspend No." | i + 1;
yield();
}signal( this >lock );
}
void main( Second * this ) {
wait( this >lock );for ( int i = 0; i < 10; i += 1 ) {
sout | "Second : Suspend No." | i + 1;
yield();}
}
int main( void ) {
signal once lock;sout | "User main begin";
{
processor p;{
First f = { &lock };Second s = { &lock };
}
}sout | "User main end";
}
Figure 9: Simple Tasks
Overloading in C
A
is very similar to overloading in C++, with the exception of the additional use, in1
C
A
, of the return type to differentiate between overloaded functions. References and exceptions in C
A
are2
heavily based on the same features from C++. The mechanism for interoperating with C code in C
A
is also3
borrowed from C++.4
Both C
A
and C++ provide generics, and the syntax is quite similar. The key difference between the two,5
is that in C++ templates are expanded at compile time for each type for which the template is instantiated,6
while in C
A
, function pointers are used to make the generic fully compilable. This means that a generic7
function can be defined in a compiled library, and still be used as expected from source.8
27.2 Go 65
27.2 Go1
Go, also commonly referred to as golang, is a programming language developed at Google in 2007 [.]. It is a2
statically typed language with syntax loosely derived from that of C, adding garbage collection, type safety,3
some structural typing capabilities, additional built-in types such as variable-length arrays and key-value4
maps, and a large standard library. (Wikipedia)5
Go and C
A
differ significantly in syntax and implementation, but the underlying core concepts of the6
two languages are aligned. Both Go and C
A
use composition and interfaces as opposed to inheritance to7
enable encapsulation and abstraction. Both languages (along with their tooling ecosystem) provide a simple8
packaging mechanism for building units of code for easy sharing and reuse. Both languages also include9
built-in light weight, user level threading concurrency features that attempt to simplify the effort and thought10
process required for writing parallel programs while maintaining high performance.11
Go has a significant runtime which handles the scheduling of its light weight threads, and performs12
garbage collection, among other tasks. C
A
uses a cooperative scheduling algorithm for its tasks, and uses13
automatic reference counting to enable advanced memory management without garbage collection. This14
results in Go requiring significant overhead to interface with C libraries while C
A
has no overhead.15
27.3 Rust16
Rust is a general-purpose, multi-paradigm, compiled programming language developed by Mozilla Research.17
It is designed to be a "safe, concurrent, practical language", supporting pure-functional, concurrent-actor[dubious18
. discuss][citation needed], imperative-procedural, and object-oriented styles.19
The primary focus of Rust is in safety, especially in concurrent programs. To enforce a high level of20
safety, Rust has added ownership as a core feature of the language to guarantee memory safety. This safety21
comes at the cost of a difficult learning curve, a change in the thought model of the program, and often some22
runtime overhead.23
Aside from those key differences, Rust and C
A
also have several similarities. Both languages support24
no overhead interoperability with C and have minimal runtimes. Both languages support inheritance and25
polymorphism through the use of interfaces (traits).26
27.4 D27
The D programming language is an object-oriented, imperative, multi-paradigm system programming language28
created by Walter Bright of Digital Mars and released in 2001. [.] Though it originated as a re-engineering29
of C++, D is a distinct language, having redesigned some core C++ features while also taking inspiration from30
other languages, notably Java, Python, Ruby, C#, and Eiffel.31
D and C
A
both start with C and add productivity features. The obvious difference is that D uses classes32
and inheritance while C
A
uses composition and interfaces. D is closer to C
A
than C++ since it is limited33
to single inheritance and also supports interfaces. Like C++, and unlike C
A
, D uses garbage collection34
and has compile-time expanded templates. D does not have any built-in concurrency constructs in the35
language, though it does have a standard library for concurrency which includes the low-level primitives for36
concurrency.37
A Syntax Ambiguities38
C has a number of syntax ambiguities, which are resolved by taking the longest sequence of overlapping39
characters that constitute a token. For example, the program fragment x+++++y is parsed as x ++ ++ + y40
66 B C Incompatibles
because operator tokens ++ and + overlap. Unfortunately, the longest sequence violates a constraint on incre-1
ment operators, even though the parse x ++ + ++ y might yield a correct expression. Hence, C program-2
mers are aware that spaces have to added to disambiguate certain syntactic cases.3
In C
A
, there are ambiguous cases with dereference and operator identifiers, e.g., int *?*?(), where the4
string *?*? can be interpreted as:5
*? *? // dereference operator, dereference operator6
* ?*? // dereference, multiplication operator7
By default, the first interpretation is selected, which does not yield a meaningful parse. Therefore, C
A
does a8
lexical look-ahead for the second case, and backtracks to return the leading unary operator and reparses the9
trailing operator identifier. Otherwise a space is needed between the unary operator and operator identifier10
to disambiguate this common case.11
A similar issue occurs with the dereference, *?(...), and routine-call, ?()(...) identifiers. The ambiguity12
occurs when the deference operator has no parameters:13
*?() ... ;14
*?() ...(...) ;15
requiring arbitrary whitespace look-ahead for the routine-call parameter-list to disambiguate. However, the16
dereference operator must have a parameter/argument to dereference *?(...). Hence, always interpreting the17
string *?() as * ?() does not preclude any meaningful program.18
The remaining cases are with the increment/decrement operators and conditional expression, e.g.:19
i++? ...(...);20
i?++ ...(...);21
requiring arbitrary whitespace look-ahead for the operator parameter-list, even though that interpretation is22
an incorrect expression (juxtaposed identifiers). Therefore, it is necessary to disambiguate these cases with23
a space:24
i++ ? i : 0;25
i? ++i : 0;26
B C Incompatibles27
The following incompatibles exist between C
A
and C, and are similar to Annex C for C++ [7].28
1. Change: add new keywords29
New keywords are added to C
A
(see Section C, p. 69).30
Rationale: keywords added to implement new semantics of C
A
.31
Effect on original feature: change to semantics of well-defined feature.32
Any C11 programs using these keywords as identifiers are invalid C
A
programs.33
Difficulty of converting: keyword clashes are accommodated by syntactic transformations using the34
C
A
backquote escape-mechanism (see Section 6, p. 5).35
How widely used: clashes among new C
A
keywords and existing identifiers are rare.36
2. Change: drop K&R C declarations37
K&R declarations allow an implicit base-type of int, if no type is specified, plus an alternate38
syntax for declaring parameters. e.g.:39
x; // int x40
*y; // int *y41
f( p1, p2 ); // int f( int p1, int p2 );42
g( p1, p2 ) int p1, p2; // int g( int p1, int p2 );43
B C Incompatibles 67
C
A
continues to support K&R routine definitions:1
f( a, b, c ) // default int return2
int a, b; char c // K&R parameter declarations3
{4
...5
}6
Rationale: dropped from C11 standard.197
Effect on original feature: original feature is deprecated.8
Any old C programs using these K&R declarations are invalid C
A
programs.9
Difficulty of converting: trivial to convert to C
A
.10
How widely used: existing usages are rare.11
3. Change: type of character literal int to char to allow more intuitive overloading:12
int rtn( int i );13
int rtn( char c );14
rtn( 'x' ); // programmer expects 2nd rtn to be called15
Rationale: it is more intuitive for the call to rtn to match the second version of definition of rtn rather16
than the first. In particular, output of char variable now print a character rather than the decimal17
ASCII value of the character.18
sout | 'x' | " " | (int)'x';19
x 12020
Having to cast 'x' to char is non-intuitive.21
Effect on original feature: change to semantics of well-defined feature that depend on:22
sizeof( 'x' ) == sizeof( int )23
no long work the same in C
A
programs.24
Difficulty of converting: simple25
How widely used: programs that depend upon sizeof( 'x' ) are rare and can be changed to sizeof(char).26
4. Change: make string literals const:27
char * p = "abc"; // valid in C, deprecated in C
A
28
char * q = expr ? "abc" : "de"; // valid in C, invalid in C
A
29
The type of a string literal is changed from [] char to const [] char. Similarly, the type of a wide30
string literal is changed from [] wchar t to const [] wchar t.31
Rationale: This change is a safety issue:32
char * p = "abc";33
p[0] = 'w'; // segment fault or change constant literal34
The same problem occurs when passing a string literal to a routine that changes its argument.35
Effect on original feature: change to semantics of well-defined feature.36
Difficulty of converting: simple syntactic transformation, because string literals can be converted to37
char *.38
How widely used: programs that have a legitimate reason to treat string literals as pointers to poten-39
tially modifiable memory are rare.40
19 At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each
structure declaration and type name [6, § 6.7.2(2)]
68 B C Incompatibles
5. Change: remove tentative definitions, which only occurs at file scope:1
int i; // forward definition2
int *j = &i; // forward reference, valid in C, invalid in C
A
3
int i = 0; // definition4
is valid in C, and invalid in C
A
because duplicate overloaded object definitions at the same scope5
level are disallowed. This change makes it impossible to define mutually referential file-local6
static objects, if initializers are restricted to the syntactic forms of C. For example,7
struct X { int i; struct X *next; };8
static struct X a; // forward definition9
static struct X b = { 0, &a }; // forward reference, valid in C, invalid in C
A
10
static struct X a = { 1, &b }; // definition11
Rationale: avoids having different initialization rules for builtin types and user-defined types.12
Effect on original feature: change to semantics of well-defined feature.13
Difficulty of converting: the initializer for one of a set of mutually-referential file-local static objects14
must invoke a routine call to achieve the initialization.15
How widely used: seldom16
6. Change: have struct introduce a scope for nested types:17
enum Colour { R, G, B, Y, C, M };18
struct Person {19
enum Colour { R, G, B }; // nested type20
struct Face { // nested type21
Colour Eyes, Hair; // type defined outside (1 level)22
};23
.Colour shirt; // type defined outside (top level)24
Colour pants; // type defined same level25
Face looks[10]; // type defined same level26
};27
Colour c = R; // type/enum defined same level28
Person.Colour pc = Person.R; // type/enum defined inside29
Person.Face pretty; // type defined inside30
In C, the name of the nested types belongs to the same scope as the name of the outermost31
enclosing structure, i.e., the nested types are hoisted to the scope of the outer-most type, which32
is not useful and confusing. C
A
is C incompatible on this issue, and provides semantics similar33
to C++. Nested types are not hoisted and can be referenced using the field selection operator “.”,34
unlike the C++ scope-resolution operator “::”.35
Rationale: struct scope is crucial to C
A
as an information structuring and hiding mechanism.36
Effect on original feature: change to semantics of well-defined feature.37
Difficulty of converting: Semantic transformation.38
How widely used: C programs rarely have nest types because they are equivalent to the hoisted39
version.40
7. Change: In C++, the name of a nested class is local to its enclosing class.41
Rationale: C++ classes have member functions which require that classes establish scopes.42
Difficulty of converting: Semantic transformation. To make the struct type name visible in the scope43
of the enclosing struct, the struct tag could be declared in the scope of the enclosing struct, before44
the enclosing struct is defined. Example:45
C C
A
Keywords 69
struct Y; // struct Y and struct X are at the same scope1
struct X {2
struct Y { /* ... */ } y;3
};4
All the definitions of C struct types enclosed in other struct definitions and accessed outside the5
scope of the enclosing struct could be exported to the scope of the enclosing struct. Note: this is6
a consequence of the difference in scope rules, which is documented in 3.3.7
How widely used: Seldom.8
8. Change: remove implicit conversion of void * to or from any T * pointer:9
void foo() {10
int * b = malloc( sizeof(int) ); // implicitly convert void * to int *11
char * c = b; // implicitly convert int * to void *, and then void * to char *12
}13
Rationale: increase type safety14
Effect on original feature: deletion of semantically well-defined feature.15
Difficulty of converting: requires adding a cast (see Section E.1 for better alternatives):16
int * b = (int *)malloc( sizeof(int) );17
char * c = (char *)b;18
How widely used: Significant. Some C translators already give a warning if the cast is not used.19
9. Change: Types must be declared in declarations, not in expressions In C, a sizeof expression or cast20
expression may create a new type. For example,21
p = (void*)(struct x {int i;} *)0;22
declares a new type, struct x .23
Rationale: This prohibition helps to clarify the location of declarations in the source code.24
Effect on original feature: Deletion of a semantically welldefined feature.25
Difficulty of converting: Syntactic transformation.26
How widely used: Seldom.27
10. Change: comma expression is disallowed as subscript28
Rationale: safety issue to prevent subscripting error for multidimensional arrays: x[i,j] instead of29
x[i][j], and this syntactic form then taken by C
A
for new style arrays.30
Effect on original feature: change to semantics of well-defined feature.31
Difficulty of converting: semantic transformation of x[i,j] to x[(i,j)]32
How widely used: Seldom.33
C C
A
Keywords34
C
A
introduces the following new keywords.35
catch
catchResume
choose
coroutine
disable
dtype
enable
exception
fallthrough
fallthru
finally
forall
ftype
monitor
mutex
one t
otype
throw
throwResume
trait
try
ttype
virtual
waitfor
when
with
zero t
36
70 E.1 Storage Management
D Standard Headers1
C11 prescribes the following standard header-files [6, § 7.1.2] and C
A
adds to this list:2
C11 C
A
assert.h
complex.h
ctype.h
errno.h
fenv.h
float.h
inttypes.h
iso646.h
limits.h
locale.h
math.h
setjmp.h
signal.h
stdalign.h
stdarg.h
stdatomic.h
stdbool.h
stddef.h
stdint.h
stdio.h
stdlib.h
stdnoreturn.h
string.h
tgmath.h
threads.h
time.h
uchar.h
wchar.h
wctype.h
gmp.h
malloc.h
unistd.h3
For the prescribed head-files, C
A
uses header interposition to wraps these includes in an extern "C"; hence,4
names in these include files are not mangled (see Section 4, p. 3). All other C header files must be explicitly5
wrapped in extern "C" to prevent name mangling. This approach is different from C++ where the name-6
mangling issue is handled internally in C header-files through checks for preprocessor variable cplusplus,7
which adds appropriate extern "C" qualifiers.8
E Standard Library9
The C
A
standard-library wraps explicitly-polymorphic C routines into implicitly-polymorphic versions.10
E.1 Storage Management11
The storage-management routines extend their C equivalents by overloading, alternate names, providing12
shallow type-safety, and removing the need to specify the allocation size for non-array types.13
C storage management provides the following capabilities:14
filled after allocation with a specified character or value.15
resize an existing allocation to decreased or increased its size. In either case, new storage may or may not16
be allocated and, if there is a new allocation, as much data from the existing allocation is copied into the17
new allocation. For an increase in storage size, new storage after the copied data may be filled.18
align an allocation on a specified memory boundary, e.g., an address multiple of 64 or 128 for cache-line19
purposes.20
array the allocation size is scaled to the specified number of array elements. An array may be filled,21
resized, or aligned.22
Table 2 shows allocation routines supporting different combinations of storage-management capabilities:23
C
A
memory management extends the resize capability with the notion of sticky properties. Hence,24
initial allocation capabilities are remembered and maintained when resize requires copying. For example,25
an initial alignment and fill capability are preserved during a resize copy so the copy has the same alignment26
and extended storage is filled. Without sticky properties it is dangerous to use realloc, resulting in an idiom27
of manually performing the reallocation to maintain correctness.28
29
extern "C" {30
// C unsafe allocation31
void * malloc( size t size );32
void * calloc( size t dim, size t size );33
void * realloc( void * ptr, size t size );34
void * memalign( size t align, size t size );35
void * aligned alloc( size t align, size t size );36
int posix memalign( void ** ptr, size t align, size t size );37
E.1 Storage Management 71
fill resize alignment array
C malloc no no no no
calloc yes (0 only) no no yes
realloc copy yes no no
memalign no no yes no
aligned alloc no no yes no
posix memalign no no yes no
C
A
cmemalign yes (0 only) no yes yes
realloc copy yes yes no
alloc no no no no
alloc copy no/yes no yes
alloc no/copy/yes no/yes no yes
alloc set no/yes no yes yes
alloc align no/yes no yes yes
alloc align set no/yes no yes yes
Table 2: Allocation Routines versus Storage-Management Capabilities
void * cmemalign( size t alignment, size t noOfElems, size t elemSize ); // CFA1
2
// C unsafe initialization/copy3
void * memset( void * dest, int c, size t size );4
void * memcpy( void * dest, const void * src, size t size );5
}6
7
void * realloc( void * oaddr, size t nalign, size t size ); // CFA heap8
9
forall( dtype T | sized(T) ) {10
// C
A
safe equivalents, i.e., implicit size specification11
T * malloc( void );12
T * calloc( size t dim );13
T * realloc( T * ptr, size t size );14
T * memalign( size t align );15
T * cmemalign( size t align, size t dim );16
T * aligned alloc( size t align );17
int posix memalign( T ** ptr, size t align );18
19
// C
A
safe general allocation, fill, resize, alignment, array20
T * alloc( void );21
T * alloc( size t dim );22
T * alloc( T ptr[], size t dim );23
T * alloc set( char fill );24
T * alloc set( T fill );25
T * alloc set( size t dim, char fill );26
T * alloc set( size t dim, T fill );27
T * alloc set( size t dim, const T fill[] );28
T * alloc set( T ptr[], size t dim, char fill );29
30
T * alloc align( size t align );31
T * alloc align( size t align, size t dim );32
T * alloc align( T ptr[], size t align ); // aligned realloc array33
72 E.2 String to Value Conversion
T * alloc align( T ptr[], size t align, size t dim ); // aligned realloc array1
T * alloc align set( size t align, char fill );2
T * alloc align set( size t align, T fill );3
T * alloc align set( size t align, size t dim, char fill );4
T * alloc align set( size t align, size t dim, T fill );5
T * alloc align set( size t align, size t dim, const T fill[] );6
T * alloc align set( T ptr[], size t align, size t dim, char fill );7
8
// C
A
safe initialization/copy, i.e., implicit size specification9
T * memset( T * dest, char fill );10
T * memcpy( T * dest, const T * src );11
12
// C
A
safe initialization/copy, i.e., implicit size specification, array types13
T * amemset( T dest[], char fill, size t dim );14
T * amemcpy( T dest[], const T src[], size t dim );15
}16
17
// C
A
allocation/deallocation and constructor/destructor, non array types18
forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );19
forall( dtype T | sized(T) | { void ∧?{}( T & ); } ) void delete( T * ptr );20
forall( dtype T, ttype Params | sized(T) | { void ∧?{}( T & ); void delete( Params ); } )21
void delete( T * ptr, Params rest );22
23
// C
A
allocation/deallocation and constructor/destructor, array types24
forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size t dim, Params p );25
forall( dtype T | sized(T) | { void ∧?{}( T & ); } ) void adelete( size t dim, T arr[] );26
forall( dtype T | sized(T) | { void ∧?{}( T & ); }, ttype Params | { void adelete( Params ); } )27
void adelete( size t dim, T arr[], Params rest );28
E.2 String to Value Conversion29
int ato( const char * ptr );30
unsigned int ato( const char * ptr );31
long int ato( const char * ptr );32
unsigned long int ato( const char * ptr );33
long long int ato( const char * ptr );34
unsigned long long int ato( const char * ptr );35
float ato( const char * ptr );36
double ato( const char * ptr );37
long double ato( const char * ptr );38
float Complex ato( const char * ptr );39
double Complex ato( const char * ptr );40
long double Complex ato( const char * ptr );41
42
int strto( const char * sptr, char ** eptr, int base );43
unsigned int strto( const char * sptr, char ** eptr, int base );44
long int strto( const char * sptr, char ** eptr, int base );45
unsigned long int strto( const char * sptr, char ** eptr, int base );46
long long int strto( const char * sptr, char ** eptr, int base );47
unsigned long long int strto( const char * sptr, char ** eptr, int base );48
float strto( const char * sptr, char ** eptr );49
double strto( const char * sptr, char ** eptr );50
long double strto( const char * sptr, char ** eptr );51
E.3 Search / Sort 73
float Complex strto( const char * sptr, char ** eptr );1
double Complex strto( const char * sptr, char ** eptr );2
long double Complex strto( const char * sptr, char ** eptr );3
E.3 Search / Sort4
forall( otype T | { int ?<?( T, T ); } ) // location5
T * bsearch( T key, const T * arr, size t dim );6
7
forall( otype T | { int ?<?( T, T ); } ) // position8
unsigned int bsearch( T key, const T * arr, size t dim );9
10
forall( otype T | { int ?<?( T, T ); } )11
void qsort( const T * arr, size t dim );12
13
forall( otype E | { int ?<?( E, E ); } ) {14
E * bsearch( E key, const E * vals, size t dim ); // location15
size t bsearch( E key, const E * vals, size t dim );// position16
E * bsearchl( E key, const E * vals, size t dim );17
size t bsearchl( E key, const E * vals, size t dim );18
E * bsearchu( E key, const E * vals, size t dim );19
size t bsearchu( E key, const E * vals, size t dim );20
}21
22
forall( otype K, otype E | { int ?<?( K, K ); K getKey( const E & ); } ) {23
E * bsearch( K key, const E * vals, size t dim );24
size t bsearch( K key, const E * vals, size t dim );25
E * bsearchl( K key, const E * vals, size t dim );26
size t bsearchl( K key, const E * vals, size t dim );27
E * bsearchu( K key, const E * vals, size t dim );28
size t bsearchu( K key, const E * vals, size t dim );29
}30
31
forall( otype E | { int ?<?( E, E ); } ) {32
void qsort( E * vals, size t dim );33
}34
E.4 Absolute Value35
unsigned char abs( signed char );36
int abs( int );37
unsigned long int abs( long int );38
unsigned long long int abs( long long int );39
float abs( float );40
double abs( double );41
long double abs( long double );42
float abs( float Complex );43
double abs( double Complex );44
long double abs( long double Complex );45
forall( otype T | { void ?{}( T *, zero t ); int ?<?( T, T ); T ?( T ); } )46
T abs( T );47
74 F.1 General
E.5 Random Numbers1
void srandom( unsigned int seed );2
char random( void );3
char random( char u ); // [0,u)4
char random( char l, char u ); // [l,u)5
int random( void );6
int random( int u ); // [0,u)7
int random( int l, int u ); // [l,u)8
unsigned int random( void );9
unsigned int random( unsigned int u ); // [0,u)10
unsigned int random( unsigned int l, unsigned int u ); // [l,u)11
long int random( void );12
long int random( long int u ); // [0,u)13
long int random( long int l, long int u ); // [l,u)14
unsigned long int random( void );15
unsigned long int random( unsigned long int u ); // [0,u)16
unsigned long int random( unsigned long int l, unsigned long int u ); // [l,u)17
float random( void ); // [0.0, 1.0)18
double random( void ); // [0.0, 1.0)19
float Complex random( void ); // [0.0, 1.0)+[0.0, 1.0)i20
double Complex random( void ); // [0.0, 1.0)+[0.0, 1.0)i21
long double Complex random( void ); // [0.0, 1.0)+[0.0, 1.0)i22
E.6 Algorithms23
forall( otype T | { int ?<?( T, T ); } ) T min( T t1, T t2 );24
forall( otype T | { int ?>?( T, T ); } ) T max( T t1, T t2 );25
forall( otype T | { T min( T, T ); T max( T, T ); } ) T clamp( T value, T min val, T max val );26
forall( otype T ) void swap( T * t1, T * t2 );27
F Math Library28
The C
A
math-library wraps explicitly-polymorphic C math-routines into implicitly-polymorphic versions.29
F.1 General30
float ?%?( float, float );31
float fmod( float, float );32
double ?%?( double, double );33
double fmod( double, double );34
long double ?%?( long double, long double );35
long double fmod( long double, long double );36
37
float remainder( float, float );38
double remainder( double, double );39
long double remainder( long double, long double );40
41
float remquo( float, float, int * );42
double remquo( double, double, int * );43
long double remquo( long double, long double, int * );44
[ int, float ] remquo( float, float );45
[ int, double ] remquo( double, double );46
F.2 Exponential 75
[ int, long double ] remquo( long double, long double );1
2
float div( float, float, int * ); // alternative name for remquo3
double div( double, double, int * );4
long double div( long double, long double, int * );5
[ int, float ] div( float, float );6
[ int, double ] div( double, double );7
[ int, long double ] div( long double, long double );8
9
float fma( float, float, float );10
double fma( double, double, double );11
long double fma( long double, long double, long double );12
13
float fdim( float, float );14
double fdim( double, double );15
long double fdim( long double, long double );16
17
float nan( const char * );18
double nan( const char * );19
long double nan( const char * );20
F.2 Exponential21
float exp( float );22
double exp( double );23
long double exp( long double );24
float Complex exp( float Complex );25
double Complex exp( double Complex );26
long double Complex exp( long double Complex );27
28
float exp2( float );29
double exp2( double );30
long double exp2( long double );31
// float Complex exp2( float Complex );32
// double Complex exp2( double Complex );33
// long double Complex exp2( long double Complex );34
35
float expm1( float );36
double expm1( double );37
long double expm1( long double );38
39
float pow( float, float );40
double pow( double, double );41
long double pow( long double, long double );42
float Complex pow( float Complex, float Complex );43
double Complex pow( double Complex, double Complex );44
long double Complex pow( long double Complex, long double Complex );45
F.3 Logarithm46
float log( float );47
double log( double );48
long double log( long double );49
76 F.4 Trigonometric
float Complex log( float Complex );1
double Complex log( double Complex );2
long double Complex log( long double Complex );3
4
float log2( float );5
double log2( double );6
long double log2( long double );7
// float Complex log2( float Complex );8
// double Complex log2( double Complex );9
// long double Complex log2( long double Complex );10
11
float log10( float );12
double log10( double );13
long double log10( long double );14
// float Complex log10( float Complex );15
// double Complex log10( double Complex );16
// long double Complex log10( long double Complex );17
18
float log1p( float );19
double log1p( double );20
long double log1p( long double );21
22
int ilogb( float );23
int ilogb( double );24
int ilogb( long double );25
26
float logb( float );27
double logb( double );28
long double logb( long double );29
30
float sqrt( float );31
double sqrt( double );32
long double sqrt( long double );33
float Complex sqrt( float Complex );34
double Complex sqrt( double Complex );35
long double Complex sqrt( long double Complex );36
37
float cbrt( float );38
double cbrt( double );39
long double cbrt( long double );40
41
float hypot( float, float );42
double hypot( double, double );43
long double hypot( long double, long double );44
F.4 Trigonometric45
float sin( float );46
double sin( double );47
long double sin( long double );48
float Complex sin( float Complex );49
double Complex sin( double Complex );50
long double Complex sin( long double Complex );51
F.5 Hyperbolic 77
1
float cos( float );2
double cos( double );3
long double cos( long double );4
float Complex cos( float Complex );5
double Complex cos( double Complex );6
long double Complex cos( long double Complex );7
8
float tan( float );9
double tan( double );10
long double tan( long double );11
float Complex tan( float Complex );12
double Complex tan( double Complex );13
long double Complex tan( long double Complex );14
15
float asin( float );16
double asin( double );17
long double asin( long double );18
float Complex asin( float Complex );19
double Complex asin( double Complex );20
long double Complex asin( long double Complex );21
22
float acos( float );23
double acos( double );24
long double acos( long double );25
float Complex acos( float Complex );26
double Complex acos( double Complex );27
long double Complex acos( long double Complex );28
29
float atan( float );30
double atan( double );31
long double atan( long double );32
float Complex atan( float Complex );33
double Complex atan( double Complex );34
long double Complex atan( long double Complex );35
36
float atan2( float, float );37
double atan2( double, double );38
long double atan2( long double, long double );39
40
float atan( float, float ); // alternative name for atan241
double atan( double, double );42
long double atan( long double, long double );43
F.5 Hyperbolic44
float sinh( float );45
double sinh( double );46
long double sinh( long double );47
float Complex sinh( float Complex );48
double Complex sinh( double Complex );49
long double Complex sinh( long double Complex );50
51
78 F.6 Error / Gamma
float cosh( float );1
double cosh( double );2
long double cosh( long double );3
float Complex cosh( float Complex );4
double Complex cosh( double Complex );5
long double Complex cosh( long double Complex );6
7
float tanh( float );8
double tanh( double );9
long double tanh( long double );10
float Complex tanh( float Complex );11
double Complex tanh( double Complex );12
long double Complex tanh( long double Complex );13
14
float asinh( float );15
double asinh( double );16
long double asinh( long double );17
float Complex asinh( float Complex );18
double Complex asinh( double Complex );19
long double Complex asinh( long double Complex );20
21
float acosh( float );22
double acosh( double );23
long double acosh( long double );24
float Complex acosh( float Complex );25
double Complex acosh( double Complex );26
long double Complex acosh( long double Complex );27
28
float atanh( float );29
double atanh( double );30
long double atanh( long double );31
float Complex atanh( float Complex );32
double Complex atanh( double Complex );33
long double Complex atanh( long double Complex );34
F.6 Error / Gamma35
float erf( float );36
double erf( double );37
long double erf( long double );38
float Complex erf( float Complex );39
double Complex erf( double Complex );40
long double Complex erf( long double Complex );41
42
float erfc( float );43
double erfc( double );44
long double erfc( long double );45
float Complex erfc( float Complex );46
double Complex erfc( double Complex );47
long double Complex erfc( long double Complex );48
49
float lgamma( float );50
double lgamma( double );51
F.7 Nearest Integer 79
long double lgamma( long double );1
float lgamma( float, int * );2
double lgamma( double, int * );3
long double lgamma( long double, int * );4
5
float tgamma( float );6
double tgamma( double );7
long double tgamma( long double );8
F.7 Nearest Integer9
float floor( float );10
double floor( double );11
long double floor( long double );12
13
float ceil( float );14
double ceil( double );15
long double ceil( long double );16
17
float trunc( float );18
double trunc( double );19
long double trunc( long double );20
21
float rint( float );22
long double rint( long double );23
long int rint( float );24
long int rint( double );25
long int rint( long double );26
long long int rint( float );27
long long int rint( double );28
long long int rint( long double );29
30
long int lrint( float );31
long int lrint( double );32
long int lrint( long double );33
long long int llrint( float );34
long long int llrint( double );35
long long int llrint( long double );36
37
float nearbyint( float );38
double nearbyint( double );39
long double nearbyint( long double );40
41
float round( float );42
long double round( long double );43
long int round( float );44
long int round( double );45
long int round( long double );46
long long int round( float );47
long long int round( double );48
long long int round( long double );49
50
long int lround( float );51
80 G.1 Duration
long int lround( double );1
long int lround( long double );2
long long int llround( float );3
long long int llround( double );4
long long int llround( long double );5
F.8 Manipulation6
float copysign( float, float );7
double copysign( double, double );8
long double copysign( long double, long double );9
10
float frexp( float, int * );11
double frexp( double, int * );12
long double frexp( long double, int * );13
14
float ldexp( float, int );15
double ldexp( double, int );16
long double ldexp( long double, int );17
18
[ float, float ] modf( float );19
float modf( float, float * );20
[ double, double ] modf( double );21
double modf( double, double * );22
[ long double, long double ] modf( long double );23
long double modf( long double, long double * );24
25
float nextafter( float, float );26
double nextafter( double, double );27
long double nextafter( long double, long double );28
29
float nexttoward( float, long double );30
double nexttoward( double, long double );31
long double nexttoward( long double, long double );32
33
float scalbn( float, int );34
double scalbn( double, int );35
long double scalbn( long double, int );36
37
float scalbln( float, long int );38
double scalbln( double, long int );39
long double scalbln( long double, long int );40
G Time Keeping41
G.1 Duration42
struct Duration {43
int64 t tv; // nanoseconds44
};45
46
void ?{}( Duration & dur );47
void ?{}( Duration & dur, zero t );48
G.1 Duration 81
1
Duration ?=?( Duration & dur, zero t );2
3
Duration +?( Duration rhs );4
Duration ?+?( Duration & lhs, Duration rhs );5
Duration ?+=?( Duration & lhs, Duration rhs );6
7
Duration ?( Duration rhs );8
Duration ? ?( Duration & lhs, Duration rhs );9
Duration ? =?( Duration & lhs, Duration rhs );10
11
Duration ?*?( Duration lhs, int64 t rhs );12
Duration ?*?( int64 t lhs, Duration rhs );13
Duration ?*=?( Duration & lhs, int64 t rhs );14
15
int64 t ?/?( Duration lhs, Duration rhs );16
Duration ?/?( Duration lhs, int64 t rhs );17
Duration ?/=?( Duration & lhs, int64 t rhs );18
double div( Duration lhs, Duration rhs );19
20
Duration ?%?( Duration lhs, Duration rhs );21
Duration ?%=?( Duration & lhs, Duration rhs );22
23
Bool ?==?( Duration lhs, Duration rhs );24
Bool ?!=?( Duration lhs, Duration rhs );25
Bool ?<? ( Duration lhs, Duration rhs );26
Bool ?<=?( Duration lhs, Duration rhs );27
Bool ?>? ( Duration lhs, Duration rhs );28
Bool ?>=?( Duration lhs, Duration rhs );29
30
Bool ?==?( Duration lhs, zero t );31
Bool ?!=?( Duration lhs, zero t );32
Bool ?<? ( Duration lhs, zero t );33
Bool ?<=?( Duration lhs, zero t );34
Bool ?>? ( Duration lhs, zero t );35
Bool ?>=?( Duration lhs, zero t );36
37
Duration abs( Duration rhs );38
39
Duration ?‘ns( int64 t nsec );40
Duration ?‘us( int64 t usec );41
Duration ?‘ms( int64 t msec );42
Duration ?‘s( int64 t sec );43
Duration ?‘s( double sec );44
Duration ?‘m( int64 t min );45
Duration ?‘m( double min );46
Duration ?‘h( int64 t hours );47
Duration ?‘h( double hours );48
Duration ?‘d( int64 t days );49
Duration ?‘d( double days );50
Duration ?‘w( int64 t weeks );51
Duration ?‘w( double weeks );52
53
82 G.5 Time
int64 t ?‘ns( Duration dur );1
int64 t ?‘us( Duration dur );2
int64 t ?‘ms( Duration dur );3
int64 t ?‘s( Duration dur );4
int64 t ?‘m( Duration dur );5
int64 t ?‘h( Duration dur );6
int64 t ?‘d( Duration dur );7
int64 t ?‘w( Duration dur );8
9
Duration max( Duration lhs, Duration rhs );10
Duration min( Duration lhs, Duration rhs );11
G.2 timeval12
void ?{}( timeval & t );13
void ?{}( timeval & t, time t sec, suseconds t usec );14
void ?{}( timeval & t, time t sec );15
void ?{}( timeval & t, zero t );16
void ?{}( timeval & t, Time time );17
18
timeval ?=?( timeval & t, zero t );19
timeval ?+?( timeval & lhs, timeval rhs );20
timeval ? ?( timeval & lhs, timeval rhs );21
Bool ?==?( timeval lhs, timeval rhs );22
Bool ?!=?( timeval lhs, timeval rhs );23
G.3 timespec24
void ?{}( timespec & t );25
void ?{}( timespec & t, time t sec, syscall slong t nsec );26
void ?{}( timespec & t, time t sec );27
void ?{}( timespec & t, zero t );28
void ?{}( timespec & t, Time time );29
30
timespec ?=?( timespec & t, zero t );31
timespec ?+?( timespec & lhs, timespec rhs );32
timespec ? ?( timespec & lhs, timespec rhs );33
Bool ?==?( timespec lhs, timespec rhs );34
Bool ?!=?( timespec lhs, timespec rhs );35
G.4 itimerval36
void ?{}( itimerval & itv, Duration alarm );37
void ?{}( itimerval & itv, Duration alarm, Duration interval );38
G.5 Time39
struct Time {40
uint64 t tv; // nanoseconds since UNIX epoch41
};42
43
void ?{}( Time & time );44
void ?{}( Time & time, zero t );45
H Clock 83
1
Time ?=?( Time & time, zero t );2
3
void ?{}( Time & time, timeval t );4
Time ?=?( Time & time, timeval t );5
6
void ?{}( Time & time, timespec t );7
Time ?=?( Time & time, timespec t );8
9
Time ?+?( Time & lhs, Duration rhs );10
Time ?+?( Duration lhs, Time rhs );11
Time ?+=?( Time & lhs, Duration rhs );12
13
Duration ? ?( Time lhs, Time rhs );14
Time ? ?( Time lhs, Duration rhs );15
Time ? =?( Time & lhs, Duration rhs );16
Bool ?==?( Time lhs, Time rhs );17
Bool ?!=?( Time lhs, Time rhs );18
Bool ?<?( Time lhs, Time rhs );19
Bool ?<=?( Time lhs, Time rhs );20
Bool ?>?( Time lhs, Time rhs );21
Bool ?>=?( Time lhs, Time rhs );22
23
char * yy mm dd( Time time, char * buf );24
char * ?‘ymd( Time time, char * buf ) { // short form25
return yy mm dd( time, buf );26
} // ymd27
28
char * mm dd yy( Time time, char * buf );29
char * ?‘mdy( Time time, char * buf ) { // short form30
return mm dd yy( time, buf );31
} // mdy32
33
char * dd mm yy( Time time, char * buf );34
char * ?‘dmy( Time time, char * buf ) { // short form35
return dd mm yy( time, buf );;36
} // dmy37
38
size t strftime( char * buf, size t size, const char * fmt, Time time );39
forall( dtype ostype | ostream( ostype ) ) ostype & ?|?( ostype & os, Time time );40
H Clock41
H.1 C time42
char * ctime( time t tp );43
char * ctime r( time t tp, char * buf );44
tm * gmtime( time t tp );45
tm * gmtime r( time t tp, tm * result );46
tm * localtime( time t tp );47
tm * localtime r( time t tp, tm * result );48
84 I Multi-precision Integers
H.2 Clock1
struct Clock {2
Duration offset; // for virtual clock: contains offset from real-time3
int clocktype; // implementation only -1 (virtual), CLOCK REALTIME4
};5
6
void resetClock( Clock & clk );7
void resetClock( Clock & clk, Duration adj );8
void ?{}( Clock & clk );9
void ?{}( Clock & clk, Duration adj );10
11
Duration getResNsec(); // with nanoseconds12
Duration getRes(); // without nanoseconds13
14
Time getTimeNsec(); // with nanoseconds15
Time getTime(); // without nanoseconds16
Time getTime( Clock & clk );17
Time ?()( Clock & clk );18
timeval getTime( Clock & clk );19
tm getTime( Clock & clk );20
I Multi-precision Integers21
C
A
has an interface to the GMP multi-precision signed-integers [19], similar to the C++ interface provided22
by GMP. The C
A
interface wraps GMP routines into operator routines to make programming with multi-23
precision integers identical to using fixed-sized integers. The CA
type name for multi-precision signed-24
integers is Int and the header file is gmp.25
void ?{}( Int * this ); // constructor/destructor26
void ?{}( Int * this, Int init );27
void ?{}( Int * this, zero t );28
void ?{}( Int * this, one t );29
void ?{}( Int * this, signed long int init );30
void ?{}( Int * this, unsigned long int init );31
void ?{}( Int * this, const char * val );32
void ∧?{}( Int * this );33
34
Int ?=?( Int * lhs, Int rhs ); // assignment35
Int ?=?( Int * lhs, long int rhs );36
Int ?=?( Int * lhs, unsigned long int rhs );37
Int ?=?( Int * lhs, const char * rhs );38
39
char ?=?( char * lhs, Int rhs );40
short int ?=?( short int * lhs, Int rhs );41
int ?=?( int * lhs, Int rhs );42
long int ?=?( long int * lhs, Int rhs );43
unsigned char ?=?( unsigned char * lhs, Int rhs );44
unsigned short int ?=?( unsigned short int * lhs, Int rhs );45
unsigned int ?=?( unsigned int * lhs, Int rhs );46
unsigned long int ?=?( unsigned long int * lhs, Int rhs );47
48
long int narrow( Int val );49
I Multi-precision Integers 85
unsigned long int narrow( Int val );1
2
int ?==?( Int oper1, Int oper2 ); // comparison3
int ?==?( Int oper1, long int oper2 );4
int ?==?( long int oper2, Int oper1 );5
int ?==?( Int oper1, unsigned long int oper2 );6
int ?==?( unsigned long int oper2, Int oper1 );7
8
int ?!=?( Int oper1, Int oper2 );9
int ?!=?( Int oper1, long int oper2 );10
int ?!=?( long int oper1, Int oper2 );11
int ?!=?( Int oper1, unsigned long int oper2 );12
int ?!=?( unsigned long int oper1, Int oper2 );13
14
int ?<?( Int oper1, Int oper2 );15
int ?<?( Int oper1, long int oper2 );16
int ?<?( long int oper2, Int oper1 );17
int ?<?( Int oper1, unsigned long int oper2 );18
int ?<?( unsigned long int oper2, Int oper1 );19
20
int ?<=?( Int oper1, Int oper2 );21
int ?<=?( Int oper1, long int oper2 );22
int ?<=?( long int oper2, Int oper1 );23
int ?<=?( Int oper1, unsigned long int oper2 );24
int ?<=?( unsigned long int oper2, Int oper1 );25
26
int ?>?( Int oper1, Int oper2 );27
int ?>?( Int oper1, long int oper2 );28
int ?>?( long int oper1, Int oper2 );29
int ?>?( Int oper1, unsigned long int oper2 );30
int ?>?( unsigned long int oper1, Int oper2 );31
32
int ?>=?( Int oper1, Int oper2 );33
int ?>=?( Int oper1, long int oper2 );34
int ?>=?( long int oper1, Int oper2 );35
int ?>=?( Int oper1, unsigned long int oper2 );36
int ?>=?( unsigned long int oper1, Int oper2 );37
38
Int +?( Int oper ); // arithmetic39
Int ?( Int oper );40
Int ∼?( Int oper );41
42
Int ?&?( Int oper1, Int oper2 );43
Int ?&?( Int oper1, long int oper2 );44
Int ?&?( long int oper1, Int oper2 );45
Int ?&?( Int oper1, unsigned long int oper2 );46
Int ?&?( unsigned long int oper1, Int oper2 );47
Int ?&=?( Int * lhs, Int rhs );48
49
Int ?|?( Int oper1, Int oper2 );50
Int ?|?( Int oper1, long int oper2 );51
Int ?|?( long int oper1, Int oper2 );52
Int ?|?( Int oper1, unsigned long int oper2 );53
86 I Multi-precision Integers
Int ?|?( unsigned long int oper1, Int oper2 );1
Int ?|=?( Int * lhs, Int rhs );2
3
Int ?∧?( Int oper1, Int oper2 );4
Int ?∧?( Int oper1, long int oper2 );5
Int ?∧?( long int oper1, Int oper2 );6
Int ?∧?( Int oper1, unsigned long int oper2 );7
Int ?∧?( unsigned long int oper1, Int oper2 );8
Int ?∧=?( Int * lhs, Int rhs );9
10
Int ?+?( Int addend1, Int addend2 );11
Int ?+?( Int addend1, long int addend2 );12
Int ?+?( long int addend2, Int addend1 );13
Int ?+?( Int addend1, unsigned long int addend2 );14
Int ?+?( unsigned long int addend2, Int addend1 );15
Int ?+=?( Int * lhs, Int rhs );16
Int ?+=?( Int * lhs, long int rhs );17
Int ?+=?( Int * lhs, unsigned long int rhs );18
Int ++?( Int * lhs );19
Int ?++( Int * lhs );20
21
Int ? ?( Int minuend, Int subtrahend );22
Int ? ?( Int minuend, long int subtrahend );23
Int ? ?( long int minuend, Int subtrahend );24
Int ? ?( Int minuend, unsigned long int subtrahend );25
Int ? ?( unsigned long int minuend, Int subtrahend );26
Int ? =?( Int * lhs, Int rhs );27
Int ? =?( Int * lhs, long int rhs );28
Int ? =?( Int * lhs, unsigned long int rhs );29
Int ?( Int * lhs );30
Int ? ( Int * lhs );31
32
Int ?*?( Int multiplicator, Int multiplicand );33
Int ?*?( Int multiplicator, long int multiplicand );34
Int ?*?( long int multiplicand, Int multiplicator );35
Int ?*?( Int multiplicator, unsigned long int multiplicand );36
Int ?*?( unsigned long int multiplicand, Int multiplicator );37
Int ?*=?( Int * lhs, Int rhs );38
Int ?*=?( Int * lhs, long int rhs );39
Int ?*=?( Int * lhs, unsigned long int rhs );40
41
Int ?/?( Int dividend, Int divisor );42
Int ?/?( Int dividend, unsigned long int divisor );43
Int ?/?( unsigned long int dividend, Int divisor );44
Int ?/?( Int dividend, long int divisor );45
Int ?/?( long int dividend, Int divisor );46
Int ?/=?( Int * lhs, Int rhs );47
Int ?/=?( Int * lhs, long int rhs );48
Int ?/=?( Int * lhs, unsigned long int rhs );49
50
[ Int, Int ] div( Int dividend, Int divisor );51
[ Int, Int ] div( Int dividend, unsigned long int divisor );52
53
J Rational Numbers 87
Int ?%?( Int dividend, Int divisor );1
Int ?%?( Int dividend, unsigned long int divisor );2
Int ?%?( unsigned long int dividend, Int divisor );3
Int ?%?( Int dividend, long int divisor );4
Int ?%?( long int dividend, Int divisor );5
Int ?%=?( Int * lhs, Int rhs );6
Int ?%=?( Int * lhs, long int rhs );7
Int ?%=?( Int * lhs, unsigned long int rhs );8
9
Int ?<<?( Int shiften, mp bitcnt t shift );10
Int ?<<=?( Int * lhs, mp bitcnt t shift );11
Int ?>>?( Int shiften, mp bitcnt t shift );12
Int ?>>=?( Int * lhs, mp bitcnt t shift );13
14
Int abs( Int oper ); // number functions15
Int fact( unsigned long int N );16
Int gcd( Int oper1, Int oper2 );17
Int pow( Int base, unsigned long int exponent );18
Int pow( unsigned long int base, unsigned long int exponent );19
void srandom( gmp randstate t state );20
Int random( gmp randstate t state, mp bitcnt t n );21
Int random( gmp randstate t state, Int n );22
Int random( gmp randstate t state, mp size t max size );23
int sgn( Int oper );24
Int sqrt( Int oper );25
26
forall( dtype istype | istream( istype ) ) istype * ?|?( istype * is, Int * mp ); // I/O27
forall( dtype ostype | ostream( ostype ) ) ostype * ?|?( ostype * os, Int mp );28
The following factorial programs contrast using GMP with the C
A
and C interfaces, where the output29
from these programs appears in Figure 10. (Compile with flag lgmp to link with the GMP library.)30
C
A
C
#include <gmp>int main( void ) {
sout | "Factorial Numbers";Int fact = 1;
sout | 0 | fact;for ( unsigned int i = 1; i <= 40; i += 1 ) {
fact *= i;
sout | i | fact;}
}
#include <gmp.h>int main( void ) {
gmp printf( "Factorial Numbers\n" );mpz t fact;
mpz init set ui( fact, 1 );
gmp printf( "%d %Zd\n", 0, fact );for ( unsigned int i = 1; i <= 40; i += 1 ) {
mpz mul ui( fact, fact, i );
gmp printf( "%d %Zd\n", i, fact );}
}
31
J Rational Numbers32
Rational numbers are numbers written as a ratio, i.e., as a fraction, where the numerator (top number) and33
the denominator (bottom number) are whole numbers. When creating and computing with rational numbers,34
results are constantly reduced to keep the numerator and denominator as small as possible.35
// implementation36
struct Rational {37
88 J Rational Numbers
Factorial Numbers0 1
1 1
2 23 6
4 245 120
6 720
7 50408 40320
9 362880
10 362880011 39916800
12 47900160013 6227020800
14 87178291200
15 130767436800016 20922789888000
17 35568742809600018 6402373705728000
19 121645100408832000
20 243290200817664000021 51090942171709440000
22 1124000727777607680000
23 2585201673888497664000024 620448401733239439360000
25 1551121004333098598400000026 403291461126605635584000000
27 10888869450418352160768000000
28 30488834461171386050150400000029 8841761993739701954543616000000
30 265252859812191058636308480000000
31 822283865417792281772556288000000032 263130836933693530167218012160000000
33 868331761881188649551819440128000000034 295232799039604140847618609643520000000
35 10333147966386144929666651337523200000000
36 37199332678990121746799944815083520000000037 13763753091226345046315979581580902400000000
38 52302261746660111176000722410007429120000000039 20397882081197443358640281739902897356800000000
40 815915283247897734345611269596115894272000000000
Figure 10: Multi-precision Factorials
REFERENCES 89
long int numerator, denominator; // invariant: denominator > 01
}; // Rational2
3
Rational rational(); // constructors4
Rational rational( long int n );5
Rational rational( long int n, long int d );6
void ?{}( Rational * r, zero t );7
void ?{}( Rational * r, one t );8
9
long int numerator( Rational r ); // numerator/denominator getter/setter10
long int numerator( Rational r, long int n );11
long int denominator( Rational r );12
long int denominator( Rational r, long int d );13
14
int ?==?( Rational l, Rational r ); // comparison15
int ?!=?( Rational l, Rational r );16
int ?<?( Rational l, Rational r );17
int ?<=?( Rational l, Rational r );18
int ?>?( Rational l, Rational r );19
int ?>=?( Rational l, Rational r );20
21
Rational ?( Rational r ); // arithmetic22
Rational ?+?( Rational l, Rational r );23
Rational ? ?( Rational l, Rational r );24
Rational ?*?( Rational l, Rational r );25
Rational ?/?( Rational l, Rational r );26
27
double widen( Rational r ); // conversion28
Rational narrow( double f, long int md );29
30
forall( dtype istype | istream( istype ) ) istype * ?|?( istype *, Rational * ); // I/O31
forall( dtype ostype | ostream( ostype ) ) ostype * ?|?( ostype *, Rational );32
References33
[1] Ada12. Programming languages – Ada ISO/IEC 8652:2012. https://www.iso.org/standard/-34
61507.html, 3rd edition, 2012. 235
[2] Richard C. Bilson. Implementing overloading and polymorphism in C∀. Master’s thesis, School36
of Computer Science, University of Waterloo, Waterloo, Ontario, Canada, N2L 3G1, 2003. http://-37
plg.uwaterloo.ca/theses/BilsonThesis.pdf. 3, 4238
[3] Walter Bright and Andrei Alexandrescu. D Programming Language. Digital Mars, 2016. http://-39
dlang.org/spec/spec.html. 240
[4] P. A. Buhr. A case for teaching multi-exit loops to beginning programmers. SIGPLAN Not., 20(11):14–41
22, November 1985. 1342
[5] P. A. Buhr, David Till, and C. R. Zarnke. Assignment as the sole means of updating objects. Softw.43
Pract. Exper., 24(9):835–870, September 1994. 3, 3744
[6] C Programming Language ISO/IEC 9889:2011-12. https://www.iso.org/standard/57853.html, 3rd45
edition, 2012. 2, 20, 22, 41, 67, 7046
90 REFERENCES
[7] C++ Programming Language ISO/IEC 14882:2014. https://www.iso.org/standard/64029.html, 4th1
edition, 2014. 2, 662
[8] Cobol14. Programming Languages – Cobol ISO/IEC 1989:2014. https://www.iso.org/standard/-3
51416.html, 2nd edition, 2014. 24
[9] G. V. Cormack and A. K. Wright. Polymorphism in the compiled language ForceOne. In Proceedings5
of the 20th Hawaii International Conference on Systems Sciences, pages 284–292, January 1987. 36
[10] G. V. Cormack and A. K. Wright. Type-dependent parameter inference. SIGPLAN Not., 25(6):127–7
136, June 1990. Proceedings of the ACM Sigplan’90 Conference on Programming Language Design8
and Implementation June 20-22, 1990, White Plains, New York, U.S.A. 3, 319
[11] Glen Jeffrey Ditchfield. Contextual Polymorphism. PhD thesis, Department of Computer Science,10
University of Waterloo, Waterloo, Ontario, Canada, N2L 3G1, 1992. http://plg.uwaterloo.ca/theses/-11
DitchfieldThesis.pdf. 312
[12] Tom Duff. Duff’s device. http://www.lysator.liu.se/c/duffs-device.html, November 1983. 1013
[13] Dominic Duggan, Gordon V. Cormack, and John Ophel. Kinded type inference for parametric over-14
loading. Acta Infomatica, 33(1):21–68, 1996. 315
[14] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference Manual. Addison-Wesley,16
Boston, 1st edition, 1990. 217
[15] Rodolfo Gabriel Esteves. C∀, a study in evolutionary design in programming languages. Master’s18
thesis, School of Computer Science, University of Waterloo, Waterloo, Ontario, Canada, N2L 3G1,19
2004. http://plg.uwaterloo.ca/theses/EstevesThesis.pdf. 320
[16] Fortran08. Programming Languages – Fortran Part 1:Base Language ISO/IEC 1539-1:2010. https://-21
www.iso.org/standard/50459.html, 3rd edition, 2010. 222
[17] Nissim Francez. Another advantage of keyword notation for parameter communication with subpro-23
grams. Communications of the ACM, 20(8):604–605, August 1977. 2924
[18] John Galletly. OCCAM 2: Including OCCAM 2.1. UCL (University College London) Press, London,25
2nd edition, 1996. 26, 4626
[19] GNU Multiple Precision Arithmetic Library. GNU, 2016. https://gmplib.org. 8427
[20] James Gosling, Bill Joy, Guy Steele, Gilad Bracha, and Alex Buckley. Java Language Specification,28
Java SE 8 edition, 2015. 229
[21] Robert Griesemer, Rob Pike, and Ken Thompson. Go Programming Language. Google, 2009. http://-30
golang.org/ref/spec. 231
[22] W. T. Hardgrave. Positional versus keyword parameter communication in programming languages.32
SIGPLAN Not., 11(5):52–58, May 1976. 2933
[23] Kathleen Jensen and Niklaus Wirth. Pascal User Manual and Report, ISO Pascal Standard. Springer–34
Verlag, 4th edition, 1991. Revised by Andrew B. Mickel and James F. Miner. 15, 1735
[24] C. H. Lindsey and S. G. van der Meulen. Informal Introduction to ALGOL 68. North-Holland, London,36
1977. 2137
[25] Barbara Liskov, Russell Atkinson, Toby Bloom, Eliot Moss, J. Craig Schaffert, Robert Scheifler, and38
Alan Snyder. CLU Reference Manual, volume 114 of Lecture Notes in Computer Science. Springer,39
New York, 1981. 26, 45, 4640
REFERENCES 91
[26] Dennis M. Ritchie. The development of the C language. SIGPLAN Not., 28(3):201–208, March 1993.1
192
[27] Rust Programming Language, 2015. https://doc.rust-lang.org/reference.html. 23
[28] Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley Series in Computer Science.4
Addison-Wesley, Boston, 1st edition, 1986. 15
[29] David W. Till. Tuples in imperative programming languages. Master’s thesis, Department of Computer6
Science, University of Waterloo, Waterloo, Ontario, Canada, N2L 3G1, 1989. 3, 36, 37, 38, 407
[30] TIOBE Index. http://www.tiobe.com/tiobe index. 28
[31] Ben Werther and Damian Conway. A modest proposal: C++ resyntaxed. SIGPLAN Not., 31(11):74–82,9
November 1996. 310
Index
Italic page numbers give the location of the main1
entry for the referenced term. Plain page numbers2
denote uses of the indexed term. Entries for grammar3
non-terminals are italicized. A typewriter font is used4
for grammar terminals and program identifiers.5
!?, 596
*?, 597
++?, 598
+?, 599
?, 5910
help, 511
libcfa, 512
linemarks, 513
no linemarks, 514
no main, 515
no prelude, 516
prelude dir, 517
print, 518
prototypes, 519
statistics, 520
tree, 521
?, 5922
CFA, 423
L, 524
N, 525
P, 526
S, 527
debug, 428
fgnu89 inline, 429
h, 530
help, 431
l, 532
lgmp, 8733
m, 534
n, 535
nodebug, 436
nohelp, 437
noquiet, 438
p, 539
quiet, 440
std=gnu11, 441
t, 542
∼ , 1343
∼=, 1344
?!=?, 5945
?%=?, 5946
?%?, 5947
?&=?, 5948
?&?, 5949
?(), 5950
?*=?, 5951
?*?, 5952
?++, 5953
?+=?, 5954
?+?, 5955
? , 5956
? =?, 5957
? ?, 5958
?/=?, 5959
?/?, 5960
?<<=?, 5961
?<<?, 5962
?<=?, 5963
?<?, 5964
?==?, 5965
?=?, 5966
?>=?, 5967
?>>=?, 5968
?>>?, 5969
?>?, 5970
?[?], 5971
?\=?, 5972
?\?, 5973
\IeC {\textregistered }\\IeC {\textregistered }=?, 674
?\IeC {\textregistered }\\IeC {\textregistered }?, 675
?∧=?, 5976
?∧?, 5977
?|=?, 5978
?|?, 5979
CFA MAJOR , 480
CFA MINOR , 481
CFA PATCH , 482
CFA , 483
CFORALL , 484
cforall, 485
∼, 1386
∼=, 1387
∼?, 5988
92
INDEX 93
abs, 731
acos, 772
acosh, 783
Ada, 2, 6, 324
address, 205
duality, 216
address duality, 237
addressing errors, 238
adelete, 729
aggregate, 1310
aggregation, 34, 3611
Algol68, 2112
aliasing, 3413
aligned alloc, 7014
alloc, 7115
alloc set, 7116
altexpr, 517
anew, 7218
arguments19
default, 2920
named, 2921
array, 2322
reference, 2323
ASCII, 4824
extended, 4825
ascodegen, 526
asin, 7727
asinh, 7828
assert.h, 7029
assignment, 2330
pointer, 2131
ast, 532
astdecl, 533
asterr, 534
astexpr, 535
astgen, 536
atan, 7737
atan2, 7738
atanh, 7839
ato, 7240
backward-compatible, 341
Bilson, Richard, 342
bin, 5043
bit field, 1944
box, 545
brand, 6146
break, 1347
break48
labelled, 1349
bsearch, 7350
bsearchl, 7351
bsearchu, 7352
C linkage, 353
C++, 1–3, 23, 32, 61, 62, 68, 7054
C-for-all, 155
C11, 256
calling convention, 357
calloc, 7058
cancellation59
pointer/reference, 2260
cbrt, 7661
ceil, 7962
cfa, 4, 563
C
A
, 164
clamp, 7465
closing coercion, 4466
cmemalign, 7167
Cobol, 268
codegen, 569
coercion, 2370
comma expression, 3371
compilation72
cfa, 473
compilation option74
CFA, 475
debug, 476
fgnu89 inline, 477
help, 478
nodebug, 479
nohelp, 480
noquiet, 481
quiet, 482
std=gnu11, 483
complex, 784
complex.h, 7085
const hell, 2586
constant87
underscore, 688
continue, 1389
continue90
labelled, 1391
copysign, 8092
coroutines, 6193
cos, 7794
94 INDEX
cosh, 781
counters,heap,time,all,none, 52
ctordtor, 53
ctype.h, 704
D, 2, 61, 655
Dave Till, 36
declstats, 57
default arguments, 298
delete, 729
describe not prescribe, 110
Ditchfield, Glen, 311
div, 34, 7512
Doug Lea, 2613
duality, 2114
Duff’s device, 10, 1115
erf, 7816
erfc, 7817
errno.h, 7018
Esteves, Rodolfo, 319
excl( const char * scanset, char * s ), 5420
exp, 7521
exp2, 7522
expm1, 7523
exponentiation24
floating, 725
logarithm, 726
operator, 627
unsigned integral, 728
eye candy, 1329
fdim, 7530
fenv.h, 7031
Fibonacci, 6132
flattening coercion, 4533
float.h, 7034
floor, 7935
fma, 7536
fmod, 7437
Fortran, 238
frexp, 8039
fstream, 1, 440
fstream.hfa, 4741
garbage-collection, 142
regional, 143
gcc, 4, 5, 6044
Glen Ditchfield, 345
gmp, 84, 8746
gmp.h, 70, 8747
gnu, 448
Go, 2, 61, 62, 6549
goto, 1350
goto51
restricted, 1352
hex, 5153
hypot, 7654
I/O55
common case, 4756
separator, 4857
ignore( T & val ), 5358
ilogb, 7659
immutable, 2160
implicit output separation, 4861
implicit referencing, 2462
implicit separator, 4863
incl( const char * scanset, char * s ), 5364
initialization, 2365
input/output stream library, 4766
instruction decoding, 2167
Int, 8468
interoperability, 369
interposition, 570
inttypes.h, 7071
iostream, 172
iso646.h, 7073
Java, 2, 6, 23, 32, 6574
K-W C, 375
labelled76
break, 1377
continue, 1378
lambda, 3379
ldexp, 8080
Lea, Doug, 2681
left, 5282
level number, 483
lexical list, 4384
lgamma, 7885
library support, 386
limits.h, 7087
locale.h, 7088
log, 7589
INDEX 95
log10, 761
log1p, 762
log2, 763
logarithm, 74
logb, 765
lrint, 796
lround, 797
lvalue, 22, 25, 45, 468
malloc, 709
malloc.h, 7010
mangling11
name, 3, 7012
manipulator, 48, 50, 5313
bin, 5014
excl, 5415
hex, 5116
ignore, 5317
incl, 5318
left, 5219
nl, 5020
nlOff, 5021
nlOn, 5022
nobase, 5123
nodp, 5124
oct, 5125
pad0, 5226
sci, 5127
sep, 4928
sepDisable, 4929
sepEnable, 4930
sepGet, 4931
sepGetTuple, 4932
sepOff, 4933
sepOn, 4934
sepSet, 4935
sepSetTuple, 4936
sepTuple, 4937
sign, 5138
skip, 5339
upcase, 5140
wd, 5141
wdi, 5342
ws, 5243
manipulators, 5044
mass, 3745
math.h, 7046
max, 7447
memalign, 7048
memcpy, 71, 7249
memory fault, 2150
memset, 71, 7251
Michael Tiemann, 2652
min, 7453
modf, 34, 8054
multi-level exit, 1355
multi-precision, 8456
multiple, 3757
multiple derivation, 1958
name hiding, 3259
name mangling, 360
named arguments, 2961
named return values, 2762
naming, 3463
nan, 7564
nearbyint, 7965
nested control-structure, 1366
new, 7267
newline separation, 5068
nextafter, 8069
nexttoward, 8070
nl, 5071
nlOff, 5072
nlOn, 5073
nobase, 5174
nodp, 5175
null-pointer constant, 2076
object, 2177
object-oriented, 1, 1778
oct, 5179
onion, 1980
opening coercion, 4581
operator82
exponentiation, 683
out parameter, 3484
overload, 385
pad0, 5286
parametric-polymorphic, 387
parse, 588
particle and wave, 2589
pointer, 19–2190
assignment, 2191
cancellation, 2292
pointer type, 2093
96 INDEX
posix memalign, 701
pow, 752
pow(x,y), 63
power of a name, 34
prelude, 45
preprocessor variables6
CFA MINOR , 47
CFA PATCH , 48
CFA , 49
CFA , 410
CFORALL , 411
cforall, 412
prescribing, 2013
pretty, 514
productivity, 1, 1915
Python, 1, 47, 6516
qsort, 7317
random, 7418
Rational, 8719
realloc, 7020
reference, 2121
reference type, 20, 2222
referenced type, 2023
regional garbage-collection, 124
remainder, 7425
remquo, 7426
resolver, 527
return list, 2728
Richard Bilson, 329
rint, 7930
Rodolfo Esteves, 331
round, 7932
routine object, 2533
rproto, 534
rsteps, 535
Rust, 2, 61, 6536
rvalue, 22, 2537
safety, 1, 1938
scalbln, 8039
scalbn, 8040
sci, 5141
sentinel value, 2042
sep, 4943
sepDisable, 4944
sepEnable, 4945
sepGet, 4946
sepGetTuple, 4947
sepOff, 4948
sepOn, 4949
sepSet, 4950
sepSetTuple, 4951
sepTuple, 4952
setjmp.h, 7053
shell pipe-operator, 4754
sign, 5155
signal.h, 7056
sin, 7657
sinh, 7758
skip( const char * pattern ), 5359
sound, 2060
sout, 161
sqrt, 7662
srandom, 7463
static multi-level exit, 1364
stdalign.h, 7065
stdarg.h, 7066
stdatomic.h, 7067
stdbool.h, 7068
stddef.h, 7069
stdint.h, 7070
stdio.h, 1, 4, 7071
stdlib.h, 7072
stdnoreturn.h, 7073
sticky properties, 7074
stream library, 4775
string.h, 7076
structuring coercion, 4577
swap, 7478
symevt, 579
tan, 7780
tanh, 7881
tentative definitions, 6882
tgamma, 7983
tgmath.h, 7084
threads.h, 7085
Tiemann, Michael, 2686
Till, Dave, 387
time.h, 7088
translator option89
help, 590
libcfa, 591
linemarks, 592
no linemarks, 593
INDEX 97
no main, 51
no prelude, 52
print, 53
print4
altexpr, 55
ascodegen, 56
astdecl, 57
asterr, 58
astgen, 59
ast, 510
box, 511
codegen, 512
ctordtor, 513
declstats, 514
parse, 515
pretty, 516
resolver, 517
rproto, 518
rsteps, 519
symevt, 520
tree, 521
tuple, 522
prototypes, 523
statistics24
counters,heap,time,all,none, 525
tree, 526
L, 527
N, 528
P, 529
altexpr, 530
ascodegen, 531
astdecl, 532
asterr, 533
astexpr, 534
astgen, 535
ast, 536
box, 537
codegen, 538
ctordtor, 539
declstats, 540
parse, 541
pretty, 542
resolver, 543
rproto, 544
rsteps, 545
symevt, 546
tree, 547
tuple, 548
S49
counters,heap,time,all,none, 550
h, 551
l, 552
m, 553
n, 554
p, 555
t, 556
tree, 557
trunc, 7958
tuple, 33, 4759
tuple, 560
tuple assignment, 3761
tuple expression, 3562
tuple type, 35, 4363
tuple variable, 3564
tuple-returning functions, 3565
type hoisting, 3266
type nesting, 3267
uchar.h, 7068
undefined, 21, 2869
underscore, 670
unistd.h, 7071
unsound, 2172
upcase, 5173
version number, 474
warning, 2675
wchar.h, 7076
wctype.h, 7077
wd, 5178
wdi, 5379
What goes around, comes around., 380
ws( unsigned char minimum, unsigned char significant, floating point81
5282