Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
27d04e4
Implement Solidity-style source syntax
Y-Nak Jul 23, 2026
2720c9d
Migrate source corpus to new syntax
Y-Nak Jul 23, 2026
94c37e2
Document the new source syntax
Y-Nak Jul 23, 2026
6d9076d
Validate Yul control-flow syntax
Y-Nak Jul 23, 2026
05b47bb
Preserve new syntax semantics through the frontend
Y-Nak Jul 23, 2026
6d8cf73
Harden new-syntax migration
Y-Nak Jul 23, 2026
c81c76c
Regenerate syntax railroad diagrams
Y-Nak Jul 23, 2026
fb1862a
Harden railroad diagram generation
Y-Nak Jul 23, 2026
e6eb606
Document audited syntax behavior
Y-Nak Jul 23, 2026
b79493d
Add hermetic syntax validation checks
Y-Nak Jul 23, 2026
5a28d57
Disambiguate cast targets from operators
Y-Nak Jul 24, 2026
93cdf7d
Preserve empty return clauses in semantic pretty output
Y-Nak Jul 24, 2026
e961405
Escape Yul strings in pretty output
Y-Nak Jul 24, 2026
e3b8f1a
Preserve Yul meta expressions in pretty output
Y-Nak Jul 24, 2026
a0ccb5c
Preserve nested comments during syntax migration
Y-Nak Jul 24, 2026
3aae89e
Preserve function type visibility during migration
Y-Nak Jul 24, 2026
7c9b8fd
Preserve explicit unit returns during migration
Y-Nak Jul 24, 2026
0a8804d
Protect Yul meta payloads during migration
Y-Nak Jul 24, 2026
1afc6fd
Reject loop control outside loop bodies
Y-Nak Jul 24, 2026
25d696c
Evaluate compound-assignment lvalues once
Y-Nak Jul 24, 2026
fae7aab
Reject qualified names in trait declarations
Y-Nak Jul 24, 2026
96fd8bf
Implement explicit Typedef conversions
Y-Nak Jul 24, 2026
05ab3ce
Preserve complete qualified name paths
Y-Nak Jul 24, 2026
44c90b7
Validate Yul literal representations
Y-Nak Jul 24, 2026
06f9836
Support UFCS on arbitrary value receivers
Y-Nak Jul 24, 2026
8c191de
Allow qualified generic-derivation pragma targets
Y-Nak Jul 25, 2026
267d23b
Complete rich syntax constructor migration
Y-Nak Jul 25, 2026
9e74a3b
Make compound-assignment tests self-contained
Y-Nak Jul 25, 2026
202d1d8
Derive instances for nested syntax declarations
Y-Nak Jul 25, 2026
168410d
Preserve located field accesses during lowering
Y-Nak Jul 25, 2026
a6ad608
Preserve contract shell semantics through lowering
Y-Nak Jul 25, 2026
9e4ef6d
Emit rich signature metadata in contract ABIs
Y-Nak Jul 25, 2026
dc2527b
Align omitted-return syntax expectations
Y-Nak Jul 25, 2026
4cf669a
Merge branch 'main' into new-syntax
Y-Nak Jul 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 10 additions & 10 deletions blog-post/erc20.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import assign;

function caller() -> address {
function caller() returns (address) {
let res: word;
assembly {
res := caller()
}
return address(res);
}

function myrevert(msg: word) -> () {
function myrevert(msg: word) returns (()) {
assembly { mstore(0, msg) revert(0, 32) }
}

Expand All @@ -21,21 +21,21 @@ contract MiniERC20 {
owner : address;
decimals : uint;
totalSupply : uint;
balances : mapping(address,uint);
allowance : mapping(address, mapping(address, uint));
balances : mapping(address => uint);
allowance : mapping(address => mapping(address => uint));

function mint(amount:uint) -> () {
function mint(amount:uint) returns (()) {
balances[owner] = Num.add(balances[owner], amount);
totalSupply = Num.add(totalSupply, amount);
}

function transferFrom(src:address, dst:address, amt:uint) -> bool {
function transferFrom(src:address, dst:address, amt:uint) returns (bool) {
let msg_sender = caller();
require( balances[src] >= amt /* "token/insufficient-balance" */
, 0x746f6b656e2f696e73756666696369656e742d62616c616e6365
);

if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal():uint)) {
if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal() as uint)) {
require( allowance[src][msg_sender] >= amt /* "token/insufficient-allowance" */
, 0x746f6b656e2f696e73756666696369656e742d616c6c6f77616e6365
);
Expand All @@ -46,18 +46,18 @@ contract MiniERC20 {
return true;
}

function approve(usr: address, amt: uint) -> bool {
function approve(usr: address, amt: uint) returns (bool) {
let msg_sender = caller();
allowance[msg_sender][usr] = amt;
return true;
}

function init() -> () {
function init() returns (()) {
owner = address(0x123456789abcdef);
decimals = Num.fromWord(18);
}

function main() -> uint {
function main() returns (uint) {
let msg_sender = caller();
init();
mint(uint(1000));
Expand Down
19 changes: 8 additions & 11 deletions blog-post/payment.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
data address = address(word);
enum address { address(word) }

data tokenid = tokenid(word);
enum tokenid { tokenid(word) }

data Payment =
Native(address, word)
| ERC20 (address, address, address, word)
| ERC721(address, address, address, tokenid);
enum Payment { Native(address, word), ERC20(address, address, address, word), ERC721(address, address, address, tokenid) }

function processPayment(payment : Payment) {
match payment {
| Native(to, amount) =>
match (payment ) {
case Native(to, amount) {
transfer(to, amount);
| ERC20(token, from, to, amount) =>
} case ERC20(token, from, to, amount) {
transferFromERC20(from, to, amount);
| ERC721(token, from, to, tokenId) =>
} case ERC721(token, from, to, tokenId) {
transferFromERC721(from, to, tokenId);
}
} }
}

function transfer (to : address, amount : word) {
Expand Down
32 changes: 16 additions & 16 deletions blog-post/sum.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
data uint128 = uint128(word);
enum uint128 { uint128(word) }

forall T . class T : Sum {
function sum (x : T, y : T) -> T;
trait Sum<T> {
function sum (x : T, y : T) returns (T);
}

instance uint128 : Sum {
function sum(x : uint128, y : uint128) -> uint128 {
impl Sum<uint128> {
function sum(x : uint128, y : uint128) returns (uint128) {
let res : word;
match x, y {
| uint128(n), uint128(m) =>
match (x, y ) {
case (uint128(n), uint128(m) ) {
assembly {
res := add(n,m);
res := add(n,m)
if lt(res, n) {
revert(0,0);
revert(0,0)
}
if gt(res, 0xffffffffffffffffffffffffffffffff) {
revert(0,0);
revert(0,0)
}
}
}
} }
return uint128(res);
}
}

forall T1 T2 . T1 : Sum, T2 : Sum => instance (T1,T2) : Sum {
function sum (p1 : (T1, T2), p2 (T1, T2)) -> (T1,T2) {
match p1, p2 {
| (x1,y1), (x2,y2) =>
impl<T1, T2> Sum<(T1, T2)> where T1: Sum, T2: Sum {
function sum (p1 : (T1, T2), p2 : (T1, T2)) returns ((T1, T2)) {
match (p1, p2 ) {
case ((x1,y1), (x2,y2) ) {
return (Sum.sum(x1,x2), Sum.sum(y1,y2));
}
} }
}
}
40 changes: 20 additions & 20 deletions concept-art/has-field.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
data Unit = Unit
data Pair(a, b) = Pair(a,b)
enum Unit { Unit }
enum Pair<a, b> { Pair(a, b) }

type uint = word
type string = word
type bool = word
alias uint = word;
alias string = word;
alias bool = word;

data Memory(t) = Memory(Word)
enum Memory<t> { Memory(Word) }

// this lets us link a given field in a struct to its position in it's
// underlying generic representation as a tuple.
class self:Field(prevTypes, ty) {}
trait Field<self, prevTypes, ty> {}

// this struct should desugar into the following
//struct S {
Expand All @@ -19,34 +19,34 @@ class self:Field(prevTypes, ty) {}
//}

// a type abstraction over tuples
type s = Pair(uint, Pair(string, bool))
alias s = Pair<uint, Pair<string, bool>>;

// unique types identifying each field
type sf1 = Unit
type sf2 = Unit
type sf3 = Unit
alias sf1 = Unit;
alias sf2 = Unit;
alias sf3 = Unit;

// Field instances linking each field to it's position in the underlying tuple
instance Pair(s, sf1):Field(Unit, uint) {}
instance Pair(s, sf2):Field(uint, string) {}
instance Pair(s, sf3):Field(Pair(uint, string), bool) {}
impl Field<Pair<s, sf1>, Unit, uint> {}
impl Field<Pair<s, sf2>, uint, string> {}
impl Field<Pair<s, sf3>, Pair<uint, string>, bool> {}


// struct field member access desugars into calls to this class
class self:HasField(fieldType) {
function getField(x:self) -> fieldType;
trait HasField<self, fieldType> {
function getField(x:self) returns (fieldType);
}

// we instantiate generic instances for references to types that implement Field
instance (Pair(t, fieldName):Field(prevTypes, fieldType), fieldType:ValueType) => Pair(Memory(t), fieldName):HasField(Memory(fieldType)) {
function getField(x : Pair(Memory(T), fieldName)) -> fieldType {
impl HasField<Pair<Memory<t>, fieldName>, Memory<fieldType>> where Pair<t, fieldName>: Field<prevTypes, fieldType>, fieldType: ValueType {
function getField(x : Pair<Memory<T>, fieldName>) returns (fieldType) {
// TODO: define this function...
let x : Proxy(prevTypes) = Proxy;
let x : Proxy<prevTypes> = Proxy;
let sz : Word = getMemorySize(x);
let ret : fieldType = ValueType.abs(0);
assembly {
ret := mload(add(rep(fst(x)), sz))
};
}
return ret;
}
}
53 changes: 31 additions & 22 deletions doc/module-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ This document describes the intended Solcore module and namespace system.
- the main library
- the std library
- named external libraries
- `foo.bar` maps to `foo/bar.solc`.
- `foo.bar` maps canonically to `foo/bar.sol`. The prototype may temporarily
resolve `.solc` files as an implementation detail.
- Directories are not modules.
- `foo` and `foo.bar` therefore refer to different files.
- The source file path is stored separately from module identity.
Expand All @@ -30,11 +31,11 @@ Supported forms:

```solidity
import M;
import M as A;
import M.{X, Y};
import M.{X as Z};
import M.{*};
import M.{*} hiding {X};
import * as A from M;
import {X, Y} from M;
import {X as Z} from M;
import {*} from M;
import {*} from M hiding {X};
import lib.foo.bar;
import @ext.foo.bar;
```
Expand All @@ -51,27 +52,28 @@ Import path kinds:
Current std-specific behavior:

- `import std;` resolves to the std library root from any library.
- `import std.dispatch;` resolves to `dispatch.solc` under the std root.
- `import std.dispatch;` resolves canonically to `dispatch.sol` under the std
root (the prototype may still use `dispatch.solc`).
- Bare imports do not fall back to the std root.
- Imports do not have constructor-specific selector syntax.
Exported constructors are accessed through qualified constructor paths such as `T.C`, `M.T.C`, or `alias.T.C`.

## 4. Import Visibility and Qualification

- `import M;` does not open names into unqualified scope.
- `import M as A;` binds only `A`.
- `import M.{X, Y};` imports selected exported names into unqualified scope.
- `import M.{X as Z};` imports `X` into unqualified scope as `Z`.
- `as` after a selector block, such as `import M.{X} as Z;`, is rejected.
- `import M.{*};` imports all exported item names into unqualified scope.
- `import M.{...} hiding {X, Y};` removes names from the selector result after expansion.
- `import * as A from M;` binds only `A`.
- `import {X, Y} from M;` imports selected exported names into unqualified scope.
- `import {X as Z} from M;` imports `X` into unqualified scope as `Z`.
- `as` after a selector block, such as `import {X} from M as Z;`, is rejected.
- `import {*} from M;` imports all exported item names into unqualified scope.
- `import {...} from M hiding {X, Y};` removes names from the selector result after expansion.
- Items inside `{...}` may mix simple item names and `*`.
Dotted item paths are not supported there.

Default module bindings:

- `import foo.bar;` binds `bar`.
- `import foo.bar as B;` binds `B` and does not bind `bar`.
- `import * as B from foo.bar;` binds `B` and does not bind `bar`.
- Non-alias module imports also support full-path qualification, so `import foo.bar;` allows both `bar.x` and `foo.bar.x`.
- If two imports would bind the same final segment, it is an error.
For example, `import foo.bar; import baz.bar;` is rejected.
Expand All @@ -87,6 +89,10 @@ Validation rules:

## 5. Export Syntax and Public Interfaces

The canonical new syntax deliberately leaves export and re-export spelling
undecided. The forms below document the current compiler extension; they are
not a commitment in the language syntax proposal.

Supported forms:

```solidity
Expand Down Expand Up @@ -142,10 +148,10 @@ Validation rules:
- Re-exporting two different module targets under the same public module name is rejected.
- Repeated exports of the same underlying item are normalized, so forms such as `export {main, *};` are accepted.

Instance behavior:
Impl behavior:

- Instances are import-visible whenever their defining module is imported.
- Instances are not named individually in export lists.
- Implementations are import-visible whenever their defining module is imported.
- Implementations are not named individually in export lists.

## 6. Namespaces and Name Resolution

Expand All @@ -155,7 +161,7 @@ Current duplicate checking is enforced separately for:
- contracts
- data types
- type synonyms
- classes
- traits
- the term namespace
- functions
- constructors
Expand All @@ -164,7 +170,8 @@ Current duplicate checking is enforced separately for:
Unqualified lookup order:

1. Local lexical scope
2. Current module top-level declarations and names introduced by `import M.{...}` / `import M.{*}` are treated at the same priority
2. Current module top-level declarations and names introduced by
`import {...} from M` / `import {*} from M` are treated at the same priority
3. If that combined non-local set contains more than one candidate in the same namespace, validation fails with a hard error
4. Otherwise unresolved

Expand All @@ -173,7 +180,8 @@ Current behavior:
- Local parameters and local variables still shadow non-local names.
- Current-module top-level names no longer silently shadow selected or glob-imported names.
- Selected or glob-imported names no longer silently shadow current-module top-level names.
- Re-importing the same underlying declaration is normalized, so importing `std.{*}` and then `std.{uint256}` does not fail by itself.
- Re-importing the same underlying declaration is normalized, so importing
`{*}` and then `{uint256}` from `std` does not fail by itself.

## 7. Constructors and Dot Shorthand

Expand All @@ -197,7 +205,7 @@ Examples of accepted source forms:
Current behavior:

- Bare constructor names are not resolved by default in the qualified-constructor model.
- `data Foo = Foo` remains valid because type and term namespaces are separate.
- `enum Foo { Foo }` remains valid because type and term namespaces are separate.
- A hidden constructor cannot be named from another module in either expressions or patterns.

Dot shorthand:
Expand Down Expand Up @@ -235,7 +243,8 @@ Pattern matching and exhaustiveness:
## 10. External Libraries

- External libraries are configured with `--lib NAME=DIR`.
- Source code imports them with `@NAME.module.path`.
- Source code imports them with `import @NAME.module.path;` (or another new
import form with that dotted path).
- The external library name is part of module identity.
- Relative imports inside an external library stay within that external library.
- `lib.*` inside an external library resolves from that external library's root.
Expand Down
Loading
Loading