C, Ocean Edition I

A Semantic Normalization of C

Status
Design White Paper
Edition
Ocean Edition I
Compatibility Baseline
ISO C99
Language Name
C

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:

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:

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:

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:

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:

static binds 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:

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:

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:

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:

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:

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:

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:

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:

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:

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_IDLE is a value of enum 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:

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:

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.