1. Abstract
C is one of the most successful programming languages ever created.
Its enduring success does not come from having the cleanest specification, the strongest type system, or the most internally consistent grammar.
It comes from something more fundamental.
C discovered an extraordinarily durable model of computation:
objects occupy storage
storage has addresses
values have representations
aggregates have layouts
functions manipulate values and memory
the machine remains visible
That model survived five decades of changes in processors, operating systems, compilers, and programming practice.
The language surrounding that model, however, accumulated substantial historical baggage.
Modern C contains:
- multiple declaration categories that look nearly identical,
- context-dependent type transformations,
- unspecified evaluation order,
- implementation-defined primitive behavior,
- undefined arithmetic,
- declaration syntax with redundant permutations,
- several unrelated concepts sharing individual keywords,
- obsolete optimization hints,
- old calling conventions preserved in syntax,
- textual metaprogramming preceding the language proper,
- and rules whose modern purpose is primarily compatibility with programs written decades ago.
These irregularities are not evidence that C failed.
They are evidence that C survived.
C, Ocean Edition I applies a disciplined normalization pass to the language.
It does not redesign C.
It does not modernize C by importing features from newer languages.
It does not replace pointers, structs, manual memory management, declarators, or C's machine-oriented computational model.
Instead, it asks:
Where C has several rules for something that should have one rule, what is the simplest sensible rule already implied by ordinary C usage?
Then it makes that rule universal.
Ocean Edition I therefore follows the same philosophy used when tightening Ruby into Sapphire:
Preserve the ergonomics. Preserve the personality. Remove competing interpretations.
The goal is not fewer capabilities.
The goal is fewer semantic categories.
2. The Central Principle
The governing principle of Ocean Edition I is:
Where C is irregular, ambiguous, unspecified, or historically overloaded, prefer the simplest interpretation consistent with ordinary programmer intuition and apply it universally.
This produces a language much closer to:
One Way To Do Something.
Not necessarily one syntax for every operation.
Not necessarily one implementation strategy.
Rather:
One predictable semantic rule for each piece of syntax.
Ocean Edition I prefers normalization over invention.
When C already possesses an explicit spelling for an operation, that spelling should be preferred over adding new syntax.
When two existing C forms mean nearly the same thing, one may be declared canonical.
When behavior is unspecified but a sensible deterministic rule exists, the behavior should be defined.
When a distinction exists only because of historical compiler implementation, the distinction should disappear.
3. Design Goals
Ocean Edition I exists to produce a version of C that is:
- visually C,
- spiritually C,
- mechanically close to C99,
- suitable for systems programming,
- straightforward to lower into Forge,
- trivial to interoperate with ordinary C,
- substantially easier to explain,
- substantially easier to reason about,
- and substantially less dependent upon historical folklore.
A C programmer should be able to read Ocean Edition I immediately.
A Ocean Edition I programmer should be able to return to C99 without learning another programming paradigm.
Migration in either direction should usually consist of mechanical cleanup rather than redesign.
4. Non-Goals
Ocean Edition I is not an attempt to introduce:
- ownership,
- borrowing,
- garbage collection,
- automatic reference counting,
- classes,
- methods,
- inheritance,
- interfaces,
- traits,
- generics,
- templates,
- pattern matching,
- algebraic data types,
- closures,
- exceptions,
- async/await,
- mandatory bounds checking,
- reflection,
- a managed runtime,
- or a new module ecosystem.
The namespace mechanism of section 5.1 is not a module ecosystem. It assigns names and does nothing else, leaving visibility to static and dependency to the header.
Those may belong in other languages.
They do not belong in an edition whose premise is:
C is already fundamentally enough.
5. No New Surface Language Without Necessity
Ocean Edition I should introduce effectively no general-purpose syntax.
There should be no need for:
let
var
fn
ref
ptr
internal
uninit
module
import
or similar vocabulary merely to correct C's specification.
If an irregularity can be resolved using syntax C already possesses, existing C syntax wins.
This rule is deliberately conservative.
The standard for adding syntax is much higher than the standard for tightening semantics.
5.1 The One Exception
The rule above has exactly one exception.
Ocean Edition I adds a namespace mechanism:
namespace net
{
int open(const char *host);
}
#namespace "net"
net::open(&host[0]);
#import "net.h"
This section records that decision, its reasoning, and its limits, so that the exception is visible at the place where the rule forbidding it is stated.
What problem it solves
C has one flat space of external names.
Every large C program therefore invents a prefix convention:
int net_socket_open(const char *host);
int net_socket_close(int handle);
The prefix is doing real work. It is a namespace, written by hand, spelled inconsistently across projects, invisible to the compiler, and unenforceable.
The language already asks the programmer to solve this problem. It simply refuses to help.
Why this clears the bar
The standard for adding syntax is much higher than for tightening semantics, and this addition meets it on the following grounds.
It introduces no new runtime concept. There is no new object model, no new lifetime, no new control flow, and no new calling convention.
It lowers to ordinary C identifiers. net::open has the external name net__open. The generated object file contains a symbol a C program can declare and call with no shim and no wrapper.
It leaves the ABI untouched. Nothing about layout, alignment, parameter passing, or linkage changes.
It is mechanically removable. Rewriting a namespaced program into ordinary C is a rename, which is the same rewrite the prefix convention performs by hand today.
It changes no existing program. Source containing no namespace behaves exactly as it did.
Why it does not violate the non-goals
Section 4 lists "a new module ecosystem" among the things this edition does not introduce, and that remains true.
A module system controls visibility, describes dependencies, replaces the header, and usually reaches into the build. This mechanism does none of that.
It assigns names. That is the whole of it.
Visibility is still static, exactly as before. Dependency is still the header. The build is unaffected. There is no interface file, no re-export, no import of names into scope, and no notion of a module boundary that a program can be inside or outside of.
Why #import comes with it
Section 53 says the language should not invent import syntax merely because implementation technology has improved, and that reasoning is untouched. Implementation technology is not the justification here.
#import exists because a namespace needs a way to be consumed that a textual paste cannot provide.
#include copies text into the including file. Text copied into a namespaced file would land inside that namespace, which is wrong for every header that was not written for it. Text copied out of a header carries the includer's macro state into the header, which is why C headers are written defensively.
#import is the same operation stated semantically. Same spelling, same angle brackets and quotes, same search paths. What differs is that the header is processed on its own terms and contributes its declarations rather than its characters.
That is not a module ecosystem either. It is #include with the accident of textual substitution removed.
The boundary
Nothing else has passed this test, and the test is written to be difficult to pass.
A proposed addition must solve a problem C programmers already solve by hand, must produce the same result they produce by hand, must lower to existing C constructs, must leave the ABI alone, and must be removable by a mechanical rewrite.
An addition that introduces a runtime concept fails. An addition that changes how existing programs behave fails. An addition that makes C interoperation require a shim fails.
The specification states this test normatively, and records that one facility passes it.
6. C Remains C
A normal function remains:
int add(int a, int b)
{
return a + b;
}
A structure remains:
struct Point
{
int x;
int y;
};
Pointers remain:
struct Point *point;
Control flow remains:
if (ready)
{
start();
}
else
{
stop();
}
Loops remain:
for (int i = 0; i < count; i++)
{
process(i);
}
Nothing about an ordinary source file should visually announce that a different language is being used.
7. Fewer Semantic Categories
The deepest cleanup target is not syntax.
It is C's proliferation of semantic categories.
Traditional C distinguishes among things such as:
declaration
definition
tentative definition
array object
array expression converted to pointer
function
function designator converted to pointer
automatic uninitialized object
static zero-initialized object
signed overflow
unsigned overflow
file-scope static
block-scope static
parameter static
inline definition
extern inline definition
static inline definition
Some distinctions are useful.
Many are historical.
Ocean Edition I aggressively asks whether each distinction produces real expressive power.
If it does not, the categories collapse.
8. Function Prototypes Are Always Complete
Traditional C permits:
int read();
to declare a function whose parameters are unspecified.
Ocean Edition I rejects this form.
A zero-argument function is written using the already-existing unambiguous spelling:
int read(void);
A function taking arguments declares them:
int read(void *buffer, size_t count);
A variadic function remains:
int printf(const char *format, ...);
The governing rule is:
Every function declaration completely describes its callable type.
There is no second historical function type hiding behind empty parentheses.
9. K&R Function Definitions Are Removed
Old-style definitions such as:
int add(a, b)
int a;
int b;
{
return a + b;
}
do not exist.
Only the modern C form exists:
int add(int a, int b)
{
return a + b;
}
One function syntax.
One type system.
10. auto Is Removed
This:
auto int value;
communicates nothing useful.
Automatic storage is already the normal behavior of a block-scope object:
int value;
Ocean Edition I rejects auto as a storage specifier.
A keyword whose only practical meaning is "perform the default behavior" does not justify its existence.
11. register Is Removed
This:
register int value;
belongs to an era when a source-level hint could reasonably influence physical register allocation.
Forge knows more about:
- liveness,
- register pressure,
- architecture,
- calling convention,
- vectorization,
- escape behavior,
- and optimization
than the source programmer can encode with this keyword.
The declaration becomes simply:
int value;
Register allocation belongs to the compiler.
12. static Is Retained and Narrowed
static is one of C's most overloaded words.
Ocean Edition I does not replace it with new vocabulary because both common forms are deeply characteristic of C.
At file scope:
static int counter;
means:
This declaration has translation-unit-local identity.
At block scope:
void count(void)
{
static int counter;
counter++;
}
means:
This object has persistent lifetime associated with this declaration rather than with each invocation.
These are treated as two manifestations of the same broad idea:
staticbinds persistent identity locally rather than participating in the ordinary surrounding lifetime or linkage rules.
The irregularity is tolerated because changing the spelling would make the language less C-like than the irregularity warrants.
However, unusual uses of static are removed.
In particular:
void process(int values[static 16]);
is not accepted.
static has no hidden third life as an array-parameter contract.
13. static const Has a Straightforward Meaning
Given:
static const int max_users = 64;
the declaration composes independent properties:
static local persistent identity
const cannot be modified after initialization
int object type
At file scope, this results in a translation-unit-local immutable object.
No special category named "static constant" exists.
The syntax is merely the composition of two ordinary properties.
14. const Means What Programmers Think It Means
Ocean Edition I gives const a direct rule:
An object declared const cannot be modified after initialization.
Thus:
const int x = 10;
creates an immutable object.
Casting the qualifier away does not make mutation valid:
*(int *)&x = 20;
is erroneous.
Pointer qualification remains meaningful.
Given:
int value = 10;
const int *p = &value;
the underlying object is mutable because value itself is not const.
The access path through p cannot modify it.
This gives two simple concepts:
const object
object itself is immutable
pointer to const
this access path cannot perform mutation
No folklore is required.
15. extern Has One Purpose
extern means:
This declaration refers to an externally defined object or function.
Therefore:
extern int counter;
is a declaration.
This:
extern int counter = 10;
is rejected.
Traditional C permits that form to become a definition despite being explicitly marked extern.
Ocean Edition I does not.
A definition is:
int counter = 10;
An external declaration is:
extern int counter;
One spelling for each concept.
16. Tentative Definitions Do Not Exist
Traditional C permits:
int counter;
at file scope to participate in the special category of tentative definitions.
Ocean Edition I removes that category.
At file scope:
int counter;
is a definition.
It defines storage.
Elsewhere:
extern int counter;
declares that storage.
A translation unit may not repeatedly "tentatively define" the same object and defer reconciliation to later language rules.
The distinction becomes simply:
definition
external declaration
17. inline Is Not a Linkage System
C99's inline rules are vastly more complicated than the operation they appear to describe.
Ocean Edition I treats actual inlining as an optimization decision belonging to Forge.
The useful source idiom retained is:
static inline int min(int a, int b)
{
return a < b ? a : b;
}
This expresses:
- translation-unit-local function identity,
- a complete definition suitable for headers,
- and programmer intent that the function is inexpensive to inline.
Bare external-inline forms whose behavior depends on a complicated matrix of definitions and linkage rules are rejected.
An externally visible function is simply:
int process(int value)
{
...
}
Forge may inline it regardless.
18. Declaration Syntax Is Canonical
C permits declaration specifiers in numerous equivalent orders.
For example:
unsigned long int x;
long unsigned int x;
unsigned int long x;
Ocean Edition I chooses a canonical spelling:
unsigned long x;
Likewise:
static const unsigned long flags;
extern volatile unsigned char status;
Declaration components follow the conceptual order:
storage
qualifiers
type
declarator
Redundant permutations are rejected.
This changes no expressive power.
It simply means the parser and programmer no longer need to accept every historical arrangement of the same words.
19. Primitive Type Spellings Are Canonical
The canonical primitive spellings are:
char
signed char
unsigned char
short
unsigned short
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double
long double
_Bool
void
Forms such as:
long int
unsigned long int
signed int
may be rejected where a shorter canonical spelling represents exactly the same type.
The principle is simple:
If two type spellings convey identical information, retain one.
20. C Declarators Remain
Declarations such as:
int (*handler)(const char *);
remain valid.
So does:
int (*handlers[8])(const char *);
And:
int (*matrix)[16];
These are not duplicate historical spellings.
They are the consequence of C's declarator model.
Ocean Edition I does not redesign them.
The existing C readability tool remains typedef:
typedef int handler_fn(const char *);
handler_fn *handlers[8];
This illustrates an important design distinction:
Unusual syntax is not automatically inconsistent syntax.
21. typedef Remains a Pure Alias
typedef introduces another name for an existing type:
typedef unsigned long index_t;
It does not create a nominally distinct type.
The parser may need declaration-aware symbol information to interpret typedef names.
That is acceptable.
Ocean Edition I does not sacrifice recognizable C syntax merely to make its parser theoretically prettier.
Compiler inconvenience is not itself a language defect.
22. Expressions Do Not Silently Change Type Because of Context
This is one of Ocean Edition I's most important universal rules:
An expression retains its type unless an explicit language operation converts it.
This rule removes multiple C irregularities at once.
23. Arrays Never Implicitly Decay
Given:
int values[16];
the expression:
values
denotes the array.
Its type is the array type.
Always.
If a pointer to the first element is required:
&values[0]
already expresses that operation.
Thus:
process(&values[0]);
is explicit.
No new syntax is necessary.
The language simply stops performing an invisible type transformation.
24. Array Parameters Are Not Pointers Wearing Array Syntax
Traditional C permits:
void process(int values[]);
void process(int values[16]);
and then silently adjusts both to:
void process(int *values);
Ocean Edition I rejects the fake forms.
If the parameter is a pointer:
void process(int *values);
If the parameter is a pointer to an actual array:
void process(int (*values)[16]);
The declaration says what the type is.
The compiler does not reinterpret it.
25. Functions Do Not Implicitly Become Function Pointers
Given:
int compare(int a, int b);
the expression:
compare
denotes the function.
The pointer is obtained explicitly:
&compare
Therefore:
int (*fn)(int, int) = &compare;
A direct function remains callable:
compare(1, 2);
A function pointer remains callable:
fn(1, 2);
The call operator knows how to invoke both.
The function itself need not silently become another type.
26. Evaluation Is Left-to-Right
Ocean Edition I defines one general evaluation rule:
Subexpressions evaluate from left to right unless the syntax explicitly defines a different control dependency.
Thus:
send(header(), body(), flags());
evaluates as:
header()
body()
flags()
send(...)
Likewise:
a() + b() * c()
evaluates function calls left-to-right while operator precedence still determines how the resulting values are combined.
This separates two independent concepts:
precedence
which operation consumes which value
evaluation order
when those values are produced
Short-circuit operators retain their natural semantics:
a && b
a || b
The conditional operator remains:
condition ? yes : no
No concept equivalent to "unspecified argument evaluation order" exists.
27. Sequence-Point Folklore Disappears
Because evaluation order is defined, the language does not need programmers to reason through a historical hierarchy of:
- sequence points,
- unsequenced modifications,
- indeterminately sequenced operations,
- and unspecified call evaluation.
Expressions have an explicit evaluation order.
Suspicious expressions may still be poor style.
They do not become metaphysical questions.
28. Every Object Begins With a Value
Traditional C distinguishes between:
static-storage object
implicitly zero initialized
automatic object
indeterminate unless initialized
Ocean Edition I collapses this distinction.
Every object's lifetime begins with a valid value.
If no initializer is written, zero initialization occurs.
Thus:
int count;
begins as zero.
int *pointer;
begins as a null pointer.
struct Point point;
begins with recursively zero-initialized members.
This extends a rule C already uses for static storage to all storage durations.
The syntax does not change.
The exception disappears.
29. Zero Initialization Need Not Cost Anything
The semantic guarantee does not require unnecessary machine work.
Given:
int value;
value = calculate();
return value;
Forge can prove that the initial zero is never observed.
The initialization vanishes during optimization.
The semantic model remains:
object begins valid
while generated code remains:
do only necessary work
Ocean Edition I does not introduce an uninit keyword unless real implementation evidence eventually demonstrates a substantial need for one.
The default design remains simpler without it.
30. Aggregate Initialization Is Uniform
C99 designated initializers remain:
struct Point point = {
.x = 10,
.y = 20,
};
Partial initialization zero-initializes everything not explicitly specified:
int values[8] = {
[3] = 7,
};
The rule becomes universal:
An explicitly supplied initializer sets the specified value; every unspecified part begins as zero.
31. Signed Arithmetic Has Defined Results
Traditional C gives unsigned overflow modular behavior while signed overflow is undefined.
Ocean Edition I removes that asymmetry.
Ordinary finite-width integer arithmetic behaves according to two's-complement machine arithmetic.
Thus:
INT_MAX + 1
wraps to the minimum representable int.
Unsigned arithmetic continues its ordinary modular behavior.
The optimizer may not derive facts from the proposition that signed overflow "cannot happen."
Optimization facts must come from:
- proof,
- source contracts,
- range analysis,
- or target guarantees.
Not semantic disappearance.
32. Invalid Arithmetic Has Defined Failure
Operations which cannot produce a valid result have defined trap behavior.
Examples include:
x / 0
x % 0
and shifts whose count falls outside the valid width.
The language distinguishes:
defined value
defined trap
unsafe memory access
rather than treating all invalid circumstances as arbitrary undefined behavior.
Forge may eliminate trap machinery wherever validity is statically proven.
33. Integer Representation Is Modern but ABI-Compatible
Ocean Edition I does not redefine ordinary C types into a new independent width hierarchy.
The selected target ABI continues to determine the sizes of:
short
int
long
long long
Portable programs use:
int8_t
uint8_t
int16_t
uint16_t
int32_t
uint32_t
int64_t
uint64_t
intptr_t
uintptr_t
where width matters.
This is already good C practice.
The edition should not create gratuitous ABI incompatibility merely for theoretical neatness.
34. Plain char Is Predictable
One implementation-defined behavior is sufficiently irritating and inexpensive to normalize.
Plain:
char
has a consistent non-negative byte interpretation.
Programs requiring signed small integers write:
signed char
Programs explicitly requiring unsigned small integers write:
unsigned char
Source whose correctness depends upon implementation-defined plain-char signedness was already nonportable.
The migration is mechanical.
35. Keep C's Familiar Integer Conversion Model Where Possible
Ocean Edition I should resist the temptation to redesign every arithmetic promotion.
C's integer promotions and usual arithmetic conversions are inelegant, but they are deeply embedded in:
- ordinary expressions,
- calling conventions,
- variadic functions,
- existing algorithms,
- and programmer expectations.
The design criterion is not:
Would we invent this rule today?
It is:
Does this irregularity cause enough semantic damage to justify divergence?
Where the answer is no, compatibility wins.
Ocean Edition I is a semantic tightening, not a theoretical reconstruction of arithmetic.
36. Aliasing Is Conservative by Default
Ordinary pointers may alias.
Forge does not infer non-aliasing merely because two pointers have different nominal pointee types.
When a programmer knows that accesses are independent, C already has an explicit spelling:
restrict
For example:
void blend(
float *restrict dst,
const float *restrict src,
size_t count
);
The stronger optimization contract is explicitly visible.
This follows a general principle:
Optimization rights should be expressed or proven, not extracted from semantic traps.
37. restrict Has a Direct Meaning
restrict means that accesses through the restricted pointer obey the non-aliasing contract for the relevant execution scope.
The specification should describe that contract directly.
It should not require programmers to learn committee-era terminology surrounding pointer association and effective types merely to understand what they have promised.
Forge lowers restrict into explicit alias information.
38. volatile Has One Narrow Meaning
volatile means:
Every language-level access is observable and must actually occur.
It does not mean:
- atomic,
- thread safe,
- synchronized,
- locked,
- ordered between threads,
- or a memory barrier.
This:
volatile uint32_t *status;
is appropriate for memory-mapped or externally changing state.
Concurrency primitives remain separate.
C folklore around volatile should not become part of Ocean Edition I semantics.
39. Union Storage Means Overlapping Storage
A union:
union Bits
{
float f;
uint32_t u;
};
is defined as overlapping typed storage.
Writing one member and inspecting another interprets the same underlying representation through the selected member type.
This matches the intuitive systems-programming purpose of a union.
The language does not introduce a hidden "active member" abstraction where the syntax itself represents raw overlapping storage.
40. Pointer Operations Remain Machine-Shaped
Pointers remain one of C's defining abstractions.
The syntax remains:
int *p;
&p;
*p;
p + n;
p - n;
p[i];
No ownership model is added.
No fat pointer is required.
No new reference type appears.
The edition may define more pointer behavior where ordinary hardware permits a sensible rule, but it should not invent an abstract pointer model whose primary effect is making machine-address reasoning less direct.
41. Null Pointers Keep the Familiar Spelling
Ocean Edition I does not require a new null literal.
Programs use:
NULL
The standard environment defines this as the canonical null pointer form.
Strict Ocean Edition I code should prefer:
int *pointer = NULL;
and:
if (pointer == NULL)
{
...
}
rather than relying upon integer literal zero as pointer notation.
The surface stays conventional C.
The intent becomes canonical.
42. Variable-Length Arrays Are Removed
Runtime-sized declarations such as:
int values[count];
are rejected when count is not a compile-time constant.
Arrays are fixed-size objects.
Dynamic storage is represented explicitly using pointers and allocation.
This removes:
- dynamic
sizeof, - hidden stack allocation,
- VLA parameter semantics,
- control-flow-sensitive allocation lifetime,
- and additional runtime behavior hidden inside declaration syntax.
Fixed arrays remain:
int values[64];
Flexible array members remain:
struct Packet
{
size_t length;
unsigned char payload[];
};
The latter describe trailing object storage cleanly and are therefore retained.
43. Multi-Character Character Constants Are Removed
This:
'abcd'
is implementation-defined and easily confused with a string literal.
It is rejected.
A character is:
'a'
A string is:
"abcd"
Binary tags and packed values should be assembled explicitly.
44. Trigraphs and Dead Character-Set Compatibility Are Removed
Ocean Edition I source is UTF-8.
Trigraphs do not exist.
Ancient alternate punctuation forms whose purpose was to permit writing C on character sets lacking C punctuation are unnecessary.
Source code should contain the characters it means.
This removes complexity without affecting contemporary C source.
45. Useful C99 Features Remain
Ocean Edition I is not a crusade against everything unusual.
Useful features with coherent semantics stay.
These include:
- compound literals,
- designated initializers,
- flexible array members,
goto,- assignment expressions,
- pointer arithmetic,
- unions,
- bit manipulation,
switch,- fallthrough,
typedef,- function pointers,
- and C declarators.
The question is not whether a construct is old or unfashionable.
The question is whether its rules are coherent.
46. Compound Literals Remain
This remains:
(struct Point){
.x = 10,
.y = 20,
}
It composes naturally with C's value and aggregate model.
There is no reason to remove it.
47. Designated Initializers Remain
This remains:
struct Point point = {
.y = 20,
.x = 10,
};
It is one of C99's best ergonomic improvements.
The fact that a feature is newer than K&R does not make it suspicious.
48. goto Remains
This remains:
goto cleanup;
goto is primitive control flow.
It is occasionally the clearest expression of:
- cleanup paths,
- parser state machines,
- low-level recovery,
- and resource unwinding in manual systems code.
Removing it would be language fashion, not semantic normalization.
49. Assignment Expressions Remain
Idiomatic C such as:
while ((c = getchar()) != EOF)
{
...
}
continues to work.
Assignment as an expression is deeply C-like and internally coherent.
Ocean Edition I does not remove expressive features merely because other languages choose differently.
50. Bitfields Remain, but Layout Is Explicitly ABI-Defined
Bitfields such as:
struct Flags
{
unsigned int ready : 1;
unsigned int mode : 3;
};
remain available.
Their physical arrangement is completely specified by the selected Forge ABI.
That includes:
- allocation direction,
- storage-unit boundaries,
- alignment,
- container size,
- and packing.
Bitfields are not portable wire formats unless the chosen ABI says they are.
The mistake is not the feature.
The mistake is leaving programmers unsure what layout the selected target will produce.
51. Enums Remain C-Like but Become Conceptually Cleaner
Given:
enum State
{
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED
};
the enumerators conceptually belong to:
enum State
rather than existing merely as unrelated integer constants generated by the declaration.
Ordinary integral conversion may remain where needed for C compatibility.
The semantic starting point, however, is:
STATE_IDLEis a value ofenum State.
This preserves C's permissiveness without pretending enumeration declarations do not define meaningful types.
52. The Preprocessor Is a Compatibility Problem, Not the Language Model
The C preprocessor is structurally unlike C.
It operates on tokens before semantic analysis and supports:
- textual substitution,
- function-like macros,
- token pasting,
- stringification,
- conditional source removal,
- and textual inclusion.
Ocean Edition I should avoid relying on this mechanism for ordinary abstraction.
Native code should prefer:
static const int max_users = 64;
over:
#define MAX_USERS 64
and:
static inline int max(int a, int b)
{
return a > b ? a : b;
}
over:
#define MAX(a, b) ...
However, C interoperability makes the preprocessor impossible to simply pretend away.
Forge therefore treats traditional preprocessing primarily as a compatibility boundary.
Existing headers and C code remain consumable.
Native Ocean Edition I style minimizes dependence upon macro metaprogramming.
53. Header Surface Syntax Remains Familiar
Ocean Edition I retains familiar source spelling such as:
#include <stdint.h>
#include "network.h"
Forge may internally implement native include processing structurally rather than literally pasting source text.
That is an implementation choice.
The language should not invent import syntax merely because implementation technology has improved.
Surface compatibility is valuable.
Ocean Edition I nevertheless provides #import, and the reasoning above is the reason it is spelled the way it is. The directive exists to carry namespaces rather than to modernize inclusion, it keeps the header name syntax and the search paths of #include, and #include remains available and unchanged. See section 5.1.
54. Translation Should Be Simple to Explain
Traditional C translation is specified through a long sequence of historical translation phases.
Ocean Edition I should be explainable more directly:
UTF-8 source
↓
directive processing
↓
lexing
↓
parsing
↓
semantic normalization
↓
Forge IR
Compatibility preprocessing may expand that process when consuming historical C.
The native semantic model should not require archaeological knowledge of compiler frontends.
55. Forge Should Encode the Rules Explicitly
Ocean Edition I's semantic cleanup should survive every later optimization pass.
Therefore normalization occurs early.
For:
foo(a(), b(), c());
the frontend conceptually emits:
v0 = call a
v1 = call b
v2 = call c
call foo(v0, v1, v2)
No backend pass decides evaluation order.
That question has already been answered by the language.
Likewise:
&values[0]
produces explicit address computation.
There is no generic "array decay" node.
Signed arithmetic explicitly carries wrapping semantics.
restrict explicitly produces alias guarantees.
Default initialization explicitly produces an initial value which ordinary optimization may later erase.
This makes the specification structural.
56. C99 and Ocean Edition I Converge After Semantics
Forge should retain a faithful C99 frontend alongside Ocean Edition I.
They may share:
- lexer infrastructure,
- declarator parsing,
- ABI logic,
- structure layout machinery,
- expression infrastructure,
- and backend lowering.
They should not pretend to have identical semantics.
For example:
C99:
array expression
→ context determines whether decay occurs
→ pointer
Ocean Edition I:
array expression
→ array
explicit &a[0]
→ pointer
Or:
C99:
signed overflow
→ undefined behavior
Ocean Edition I:
signed overflow
→ wrapping result
The two languages converge only after the frontend has resolved their respective contracts.
This is the correct architecture for Forge.
57. Translation Between Editions Should Be Mechanical
A major project goal should be making Ocean Edition I easy to backport into conventional C99.
For example, deterministic argument ordering:
foo(a(), b());
may lower into portable C99 as:
T0 _a = a();
T1 _b = b();
foo(_a, _b);
Explicit array addressing is already valid C:
process(&values[0]);
Canonical declarations are ordinary C.
Removing obsolete constructs produces ordinary C.
Most Ocean Edition I source should therefore already compile as conventional C99 or require trivial mechanical lowering.
Likewise, migration from C99 into Ocean Edition I should largely produce diagnostics such as:
function declaration does not specify parameters
implicit array-to-pointer conversion
implicit function-to-pointer conversion
variable-length array
obsolete register specifier
extern declaration contains initializer
duplicate file-scope definition
noncanonical declaration spelling
dependency on unspecified evaluation order
The compiler should provide precise suggested rewrites whenever possible.
58. Compatibility Is a Boundary, Not a Governing Principle
Ocean Edition I should remain excellent at calling C.
It should not remain internally strange merely because C is strange.
Forge already has the ideal tool for this separation:
historical C source
↓
C99 frontend
↓
Forge semantic representation
↑
Ocean Edition I frontend
↑
normalized C source
Legacy semantics belong in the C99 frontend.
Ocean Edition I semantics belong in the First Ocean frontend.
The backend serves both.
59. The Litmus Test
Every proposed change should face the following questions.
59.1 Does the current behavior have multiple semantic categories?
If yes, attempt to unify them.
59.2 Is there an existing C spelling that expresses the explicit operation?
If yes, prefer it.
Example:
&array[0]
instead of introducing new array-address syntax.
59.3 Is the behavior merely unspecified?
If a sensible universal rule exists, define it.
Example:
evaluation order → left-to-right
59.4 Is the behavior implementation-defined without useful modern benefit?
Normalize it.
59.5 Is the construct merely redundant?
Choose one canonical form.
59.6 Would fixing the problem require making the language visibly non-C?
If yes, reconsider whether the irregularity is worth fixing.
59.7 Does the existing feature provide real expressive value?
If yes, preserve it even if it is unusual.
59.8 Does the new rule reduce the number of concepts required to explain the language?
If not, the cleanup may have failed.
60. Examples of Semantic Normalization
The design can be summarized through representative transformations.
Functions
C:
f()
f(void)
Ocean Edition I:
f(void)
One complete prototype form.
Arrays
C:
array sometimes means array
array sometimes silently becomes pointer
Ocean Edition I:
array always means array
&array[0] means pointer
Functions as values
C:
function designator sometimes silently becomes pointer
Ocean Edition I:
function means function
&function means pointer
Globals
C:
definition
tentative definition
extern declaration
extern definition
Ocean Edition I:
definition
extern declaration
Initialization
C:
static objects begin zero
automatic objects may begin indeterminate
Ocean Edition I:
all objects begin with a value
Arithmetic
C:
unsigned overflow wraps
signed overflow undefined
Ocean Edition I:
finite-width integer arithmetic has defined machine behavior
Evaluation
C:
many orders unspecified
Ocean Edition I:
left-to-right
Aliasing
C:
optimizer derives surprising alias assumptions from type rules
Ocean Edition I:
ordinary pointers may alias
restrict explicitly says otherwise
inline
C:
several linkage-sensitive inline categories
Ocean Edition I:
static inline is the source-level header idiom
Forge decides actual inlining
Storage hints
C:
auto
register
ordinary local declaration
Ocean Edition I:
ordinary local declaration
This is the pattern.
Not feature removal for its own sake.
Category collapse.
61. Example Ocean Edition I Program
A normal source file might be:
#include <stddef.h>
#include <stdint.h>
struct Buffer
{
uint8_t *data;
size_t length;
size_t capacity;
};
static inline size_t min_size(size_t a, size_t b)
{
return a < b ? a : b;
}
static int buffer_copy(
struct Buffer *dst,
const struct Buffer *src
)
{
if (dst == NULL || src == NULL)
{
return -1;
}
size_t count = min_size(dst->capacity, src->length);
copy_bytes(
&dst->data[0],
&src->data[0],
count
);
dst->length = count;
return 0;
}
int main(void)
{
struct Buffer buffer = {
.data = NULL,
.length = 0,
.capacity = 0,
};
run(&buffer);
return 0;
}
This does not look like a C-inspired language.
It looks like C.
The difference is that the rules underneath it are considerably less haunted.
62. The First Ocean Doctrine
The Ocean Edition I should be governed by the following doctrine:
C is already enough. Keep its machine model. Keep its pointers. Keep its structs and unions. Keep its explicit storage. Keep its braces. Keep its declarators. Keep its control flow. Keep its ability to describe hardware directly. Do not replace unusual but coherent features merely because newer languages chose differently. Where C provides multiple ways to say the same thing, choose one. Where C silently changes the meaning or type of an expression, require the explicit C operation. Where C leaves behavior unspecified and a sensible universal order exists, define it. Where C distinguishes objects for historical reasons rather than useful semantic reasons, collapse the categories. Where dangerous behavior is necessary for systems programming, keep it. Where unknowable behavior is unnecessary, remove it. Where optimization requires stronger facts, make those facts explicit. And whenever the cure would make the source stop looking like C, reconsider the cure.
63. Final Thesis
Ocean Edition I is not intended to be "C, but better" in the usual language-design sense.
It is intended to be:
C after its accumulated rules are forced to agree with one another.
Its relationship to C99 should resemble the relationship of a carefully normalized grammar to the language people were already speaking.
Most syntax survives.
Most idioms survive.
Most code survives.
What disappears are the places where a programmer learns one rule and later discovers that an apparently identical construct follows another.
The language does not become more abstract.
It becomes more literal.
An array is an array.
A function is a function.
An address is obtained with &.
A definition defines something.
An extern declaration does not.
A const object is immutable.
An object begins life with a value.
Expressions execute in a defined order.
Arithmetic has defined behavior.
restrict says that aliasing is restricted.
volatile says that accesses are observable.
And a declaration means what it looks like it means.
The goal is therefore not to invent a successor to C.
It is to answer a much more interesting question:
What if C had received one final design pass after fifty years of learning what programmers actually use it for?
That language is C, Ocean Edition I.