Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
8925f41
Add module parser and graph groundwork
Y-Nak Feb 18, 2026
ff9d128
Support selective imports and direct module validation
Y-Nak Feb 18, 2026
671a82e
Validate unknown and duplicate selective imports
Y-Nak Feb 18, 2026
b8e3627
Add cycle and order checks for imports
Y-Nak Feb 18, 2026
004b74c
Introduce strict visibility and local precedence
Y-Nak Feb 19, 2026
33373d6
Add namespace scope checks and duplicate tests
Y-Nak Feb 19, 2026
774537d
Support dotted module qualifiers and precedence
Y-Nak Feb 19, 2026
581cf25
Refine strict mode flattening and alias checks
Y-Nak Feb 19, 2026
0b798ce
Add deep-path tests and remove strict-imports option
Y-Nak Feb 20, 2026
6aefa10
Migrate fixtures to qualified std imports
Y-Nak Feb 20, 2026
687e448
Adjust std fixture imports and validation behavior
Y-Nak Feb 20, 2026
fde8e58
Remove open import semantics and refresh fixtures
Y-Nak Feb 20, 2026
3e55ed9
Add export declaration syntax and loader support
Y-Nak Feb 21, 2026
ecfda32
Enforce qualified constructor path imports
Y-Nak Feb 21, 2026
7a76a05
Tighten namespace and export validation rules
Y-Nak Feb 21, 2026
e65b64f
Compile imports from declared exports across passes
Y-Nak Feb 22, 2026
97b7815
Implement dot constructor shorthand type resolution
Y-Nak Feb 22, 2026
98be4f8
Add glob import/export selectors and selector AST
Y-Nak Feb 22, 2026
fe20535
Require typing context for dot shorthand
Y-Nak Feb 23, 2026
5f40526
Document module system behavior
Y-Nak Feb 23, 2026
8123486
Type-qualify constructor resolution and fixtures
Y-Nak Feb 23, 2026
d2b9c5f
Propagate match context and fix shadowing
Y-Nak Feb 23, 2026
a40809d
Reject duplicate contract names
Y-Nak Feb 23, 2026
3551c72
Generate field helpers on Cxt collisions
Y-Nak Feb 23, 2026
f29faa6
Harden qualified function import helper injection
Y-Nak Feb 23, 2026
cc76865
Resolve module-qualified constructors
Y-Nak Feb 24, 2026
307d11e
Propagate expected types into call arguments
Y-Nak Feb 24, 2026
b7dccc1
Add import qualifier regression coverage
Y-Nak Feb 24, 2026
92dd27f
Stabilize strict module imports and fixture imports
Y-Nak Feb 24, 2026
0340975
Fix transitive import bug
Y-Nak Feb 24, 2026
0a10cac
Improve error message
Y-Nak Feb 24, 2026
22823f7
Improve module doc
Y-Nak Feb 24, 2026
19986d8
Fix incomplete dot short-hand
Y-Nak Feb 24, 2026
a493126
Fix invalid name emission
Y-Nak Feb 25, 2026
ff9ff7f
Import std correctly in contract test cases
Y-Nak Feb 25, 2026
fcf7955
Remove unnecessary pragmas
Y-Nak Feb 26, 2026
c1f8343
Preserve imported pragmas during strict flattening
Y-Nak Feb 26, 2026
b606903
Fix nested dot constructor resolution
Y-Nak Mar 2, 2026
cc47f0e
Remove unnecessary imports in `std/dispatch`
Y-Nak Mar 2, 2026
0fd6deb
Remove unnecessary `ExpDotVar` from AST
Y-Nak Mar 2, 2026
d026c75
Document else parser conflict
Y-Nak Mar 2, 2026
999c037
Remove unncessary wrapper function from `loader`
Y-Nak Mar 2, 2026
0e6074f
Improve the module system doc
Y-Nak Mar 2, 2026
8345ffb
Resolve imports by logical module path
Y-Nak Mar 11, 2026
7092933
Load export module references
Y-Nak Mar 12, 2026
1df2f53
Expand public export interfaces
Y-Nak Mar 12, 2026
8cca258
Resolve module bindings by final segment
Y-Nak Mar 12, 2026
e9ca39e
Compile reexported module interfaces
Y-Nak Mar 13, 2026
1d78eb9
Fix imported support flattening
Y-Nak Mar 13, 2026
ee1bc08
Support recursive module groups
Y-Nak Mar 13, 2026
1c7bf27
Add recursive import fixtures
Y-Nak Mar 14, 2026
fe2bad4
Add external library roots
Y-Nak Mar 14, 2026
a697ab4
Add external library fixtures
Y-Nak Mar 15, 2026
f304fb4
Remove dead loader helpers
Y-Nak Mar 15, 2026
e59bc8b
Update module system doc
Y-Nak Mar 15, 2026
5a3806f
Rename StructField class
Y-Nak Mar 16, 2026
f129cbd
Merge classes into type namespace
Y-Nak Mar 16, 2026
74dd192
Extend module import and export syntax
Y-Nak Mar 17, 2026
da60e8f
Use logical module ids in loader
Y-Nak Mar 11, 2026
c95dafc
Import dispatch from std namespace
Y-Nak Mar 17, 2026
b7e29d6
Simplify std root resolution
Y-Nak Mar 17, 2026
7f31a32
Add symlink module fixtures
Y-Nak Mar 17, 2026
9b0f7b4
Refine wildcard imports and exports
Y-Nak Mar 18, 2026
2a08105
Add explicit main library root
Y-Nak Mar 18, 2026
66f2ccc
Update module system doc
Y-Nak Mar 18, 2026
1f5ae3a
Support hiding in selective imports
Y-Nak Mar 19, 2026
7469c4e
Add import hiding fixtures
Y-Nak Mar 19, 2026
0fa672e
Reject non-local name shadowing
Y-Nak Mar 19, 2026
6c54b60
Document non-local lookup conflicts
Y-Nak Mar 20, 2026
a8417af
Parse constructor export specs
Y-Nak Mar 20, 2026
3e1d4be
Control exported constructors
Y-Nak Mar 21, 2026
258c4c0
Require wildcard for hidden constructors
Y-Nak Mar 21, 2026
442c86b
Document constructor export rules
Y-Nak Mar 21, 2026
b0174fc
Merge main into module-system
Y-Nak Mar 22, 2026
11002d1
Preserve builtin names across module imports
Y-Nak Mar 22, 2026
82bb3e6
Format
Y-Nak Mar 22, 2026
ee376d2
Clean up GHC warnings
Y-Nak Mar 22, 2026
478747c
Ignore local cabal cache
Y-Nak Mar 22, 2026
7c28a4b
Fix qualified constructor patterns
Y-Nak Mar 22, 2026
39be1b0
Remove loader external-root panic
Y-Nak Mar 22, 2026
7fe11bf
Add import coverage
Y-Nak Apr 2, 2026
7e76dd2
Add reexport coverage
Y-Nak Apr 3, 2026
90c9245
Fix hidden dot constructors
Y-Nak Apr 3, 2026
8b37dd4
Merge remote-tracking branch 'upstream/main' into module-system
Y-Nak Apr 6, 2026
7996645
Adjust test cases
Y-Nak Apr 6, 2026
1b40ade
Merge main into module-system
Y-Nak Apr 28, 2026
a41e4e0
Restore synonym helpers in `TcStmt`
Y-Nak Apr 28, 2026
000696a
Fix ownable dispatch imports
Y-Nak Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cabal-dev
.hpc
.hsenv
.cabal-sandbox/
.cabal-local/
cabal.sandbox.config
*.prof
*.aux
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ bugs. You should under no circumstances be using this in a production setting.
- A pen and paper formalization of the type system is
defined in the `./spec` directory, and pdf builds are available as artifacts from the [Spec PDF
workflow](https://github.com/argotorg/solcore/actions/workflows/spec.yml?query=branch%3Amain).
- The implemented module and namespace behavior is documented in [`doc/module-system.md`](doc/module-system.md).

Bug reports and feedback are very welcome.

Expand Down
2 changes: 1 addition & 1 deletion deps/nlohmann_json
Submodule nlohmann_json updated 226 files
239 changes: 239 additions & 0 deletions doc/module-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
# Module and Namespace System

This document describes the intended Solcore module and namespace system.

## 1. Entry File and Library Roots

- The entry file is the path passed to `-f` / `--file`.
- The main library root is configured with `--root`.
- The default main library root is the current working directory.
- The std library root is configured with `--include`. With the default options this is `std`.
- External libraries are registered explicitly with repeated `--lib NAME=DIR` flags.

## 2. Module Identity and File Mapping

- Module identity is logical, not canonical-path-based.
- A module id is `(library, logical module path)`.
- Libraries are:
- the main library
- the std library
- named external libraries
- `foo.bar` maps to `foo/bar.solc`.
- Directories are not modules.
- `foo` and `foo.bar` therefore refer to different files.
- The source file path is stored separately from module identity.
- If two logical module paths reach the same physical file, they are still treated as different modules when their module ids differ.

## 3. Import Syntax

Supported forms:

```solidity
import M;
import M as A;
import M.{X, Y};
import M.{*};
import M.{*} hiding {X};
import lib.foo.bar;
import @ext.foo.bar;
```

Import path kinds:

- `import foo.bar;`
- relative to the importing module's logical directory
- `import lib.foo.bar;`
- from the current library root
- `import @ext.foo.bar;`
- from the external library registered as `ext`

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.
- 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.{*};` imports all exported item names into unqualified scope.
- `import 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`.
- 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.

Validation rules:

- Duplicate qualifier names are rejected.
- Duplicate explicit names inside one selective import are rejected.
- Duplicate names inside one `hiding {...}` list are rejected.
- Unknown selected names are rejected.
- Unknown hidden names are rejected.
- Ambiguous names introduced by selective or glob imports are rejected.

## 5. Export Syntax and Public Interfaces

Supported forms:

```solidity
export {X, Y};
export {*};
export {T(C1, C2)};
export {T(*)};
export {M.*, *};
export M;
export M as N;
export M.{X, Y};
export M.{T(C1, C2)};
export M.{T(*)};
export M.*;
```

Current behavior:

- A module may contain multiple export declarations.
- The public interface is the union of all export declarations after expansion.
- If a module has no export declarations, its public interface is empty.
- `export` declarations do not add names to the current module's local scope.
- Only exported names and re-exported modules are visible to importers.

Expansion rules:

- `export {X}` exports a local item.
- `export {T(C1, C2)}` exports the local data type `T` and the named constructors of `T`.
- `export {T(*)}` exports the local data type `T` and all constructors of `T`.
- `export {*} ` exports all local importable top-level items, but not subordinate constructors.
- `export {M.*}` re-exports all exported item names from `M`.
- `export M;` re-exports the module under its final segment.
- `export M as N;` re-exports the module under `N`.
- `export M.{X, Y};` re-exports selected item names from `M`.
- `export M.{T(C1, C2)};` re-exports the imported data type `T` and the named constructors of `T` that are visible through `M`.
- `export M.{T(*)};` re-exports the imported data type `T` and all constructors of `T` that are visible through `M`.
- `export M.*;` re-exports all exported item names from `M`, preserving constructor visibility on exported data types, but not the module binding itself.

Constructor-export rules:

- Constructor names are not exported as standalone top-level items.
They must be exported through a data type item such as `T(C1, C2)` or `T(*)`.
- `T(C1, C2)` and `T(*)` always imply export of the type `T` itself.
- `*` never implies export of constructors.

Validation rules:

- Unknown local exports are rejected.
- Unknown re-exported names are rejected.
- Naming a constructor that does not belong to the selected data type is rejected.
- Re-exporting a constructor that is not visible through the source module is rejected.
- Re-exporting two different underlying items under the same public name is rejected.
- 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:

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

## 6. Namespaces and Name Resolution

Current duplicate checking is enforced separately for:

- the type namespace
- contracts
- data types
- type synonyms
- classes
- the term namespace
- functions
- constructors
- values

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
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

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.

## 7. Constructors and Dot Shorthand

Constructors remain term-level names, but they are canonicalized after resolution to `Type.Constructor` form.

Constructor visibility:

- `export {T}` exports the type only; external modules cannot name any constructors of `T`.
- `export {T(C1, C2)}` exports only the named constructors of `T`.
- `export {T(*)}` exports all constructors of `T`.
- Imports do not add constructors as unqualified names.
Instead, exported constructors are accessed through qualified paths such as `T.C`, `mod.T.C`, and `alias.T.C`.

Examples of accepted source forms:

- `Option.Some`
- `mod.Option.Some`
- `alias.Option.Some`
- `.Some` when expected type context is available

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.
- A hidden constructor cannot be named from another module in either expressions or patterns.

Dot shorthand:

- `.K` is resolved during contextual typing, not during the initial name-resolution pass.
- Pattern shorthand uses the expected scrutinee type.
- Expression shorthand uses the surrounding expected type.
- Nested shorthand works when outer context determines the constructor family.
- `.K` only resolves if `K` is visible from the current module on the expected data type.
- Missing expected type, non-data expected type, or missing constructor name is an error.

Pattern matching and exhaustiveness:

- Inside the defining module, pattern matching sees the full constructor set of a data type.
- Outside the defining module, only visible constructors may appear in patterns.
- If some constructors of a data type are hidden from the current module, matches on that type are treated as non-exhaustive unless they contain a wildcard or catch-all arm.
- If all constructors of a data type are visible, ordinary exhaustiveness checking applies.

## 8. Pragmas

- Pragmas are module-local.
- Pragmas do not propagate through imports.
- Imported instances are trusted during checking without importing the defining module's pragma environment.

## 9. Recursive Modules

- Import cycles are allowed.
- The loader computes strongly connected components of the import graph.
- A recursive component is compiled as a recursive module group.
- Public interfaces inside a recursive group are computed by fixed-point iteration.
- Compile surfaces for recursive groups are also computed by fixed-point iteration.
- Self-imports and mutual re-export cycles are therefore supported.
- If interface or compile-surface expansion does not stabilize, compilation fails for the group.

## 10. External Libraries

- External libraries are configured with `--lib NAME=DIR`.
- Source code imports them with `@NAME.module.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.
- Version selection and dependency lockfiles are not part of the language-level implementation yet.
2 changes: 2 additions & 0 deletions sol-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ library
Solcore.Desugarer.ReplaceFunTypeArgs
Solcore.Desugarer.UniqueTypeGen
Solcore.Frontend.Lexer.SolcoreLexer
Solcore.Frontend.Module.Identity
Solcore.Frontend.Module.Loader
Solcore.Frontend.Parser.SolcoreParser
Solcore.Frontend.Pretty.SolcorePretty
Solcore.Frontend.Pretty.Name
Expand Down
20 changes: 14 additions & 6 deletions src/Solcore/Desugarer/FieldAccess.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,30 @@ type NmEquation = Equation Name
fieldDesugarer :: CompUnit Name -> CompUnit Name
fieldDesugarer (CompUnit ims topdecls) = CompUnit ims (extras <> topdecls')
where
existingDataTypes =
Set.fromList
[ dataName dt
| TDataDef dt <- topdecls
]
(extras, topdecls') = mapAccumL go mempty topdecls
go acc (TContr c) = (acc <> extraTopDeclsForContract c, TContr (transContract c))
go acc (TContr c) =
let hasSingletonCollision =
singletonNameForContract (Contract.name c) `Set.member` existingDataTypes
in (acc <> extraTopDeclsForContract (not hasSingletonCollision) c, TContr (transContract c))
go acc v = (acc, v)

--------------------------------
-- # Extra Top Decls
--------------------------------

extraTopDeclsForContract :: NmContract -> [NmTopDecl]
extraTopDeclsForContract (Contract cname _ts cdecls) = do
extraTopDeclsForContract :: Bool -> NmContract -> [NmTopDecl]
extraTopDeclsForContract includeSingleton (Contract cname _ts cdecls) = do
let singName = singletonNameForContract cname
let contractSingDecl = TDataDef $ DataTy singName [] [Constr singName []]

let fields = getFields cdecls
let (_fieldTypes, extraFieldDecls) = foldl' (flip contractFieldStep) ([], []) fields
(contractSingDecl : extraFieldDecls)
(if includeSingleton then contractSingDecl : extraFieldDecls else extraFieldDecls)
where
-- given a list of contract field types so far and topdecls for them, amends them with data for another field
-- the types of previous fields are needed to construct field offset
Expand All @@ -71,14 +79,14 @@ extraTopDeclsForContractField cname (Field fname fty _minit) offset = [selDecl,
selName = selectorNameForField cname fname
selDecl = TDataDef $ DataTy selName [] [Constr selName []]
selType = TyCon selName []
-- instance StructField(ContractStorage(CCtx), fld1_sel):StructField(uint, ()) {}
-- instance StructField(ContractStorage(CCtx), fld1_sel):CStructField(uint, ()) {}
ctxTy = TyCon "ContractStorage" [singletonTypeForContract cname]
sfInstance =
Instance
{ instDefault = False,
instVars = [],
instContext = [],
instName = "StructField",
instName = "CStructField",
paramsTy = [translateFieldType fty, offset],
mainTy = TyCon "StructField" [ctxTy, selType],
instFunctions = []
Expand Down
2 changes: 2 additions & 0 deletions src/Solcore/Desugarer/IndirectCall.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ instance Desugar (TopDecl Name) where
desugar (TInstDef i) = TInstDef <$> desugar i
desugar (TDataDef d) = pure $ TDataDef d
desugar (TSym s) = pure $ TSym s
desugar (TExportDecl d) = pure $ TExportDecl d
desugar (TPragmaDecl d) = pure $ TPragmaDecl d
desugar (TMutualDef ms) = TMutualDef <$> desugar ms

Expand Down Expand Up @@ -145,6 +146,7 @@ instance Collect (TopDecl Name) where
collect (TInstDef _) = []
collect (TDataDef _) = []
collect (TSym _) = []
collect (TExportDecl _) = []
collect (TPragmaDecl _) = []
collect (TMutualDef ms) = collect ms

Expand Down
9 changes: 9 additions & 0 deletions src/Solcore/Frontend/Lexer/SolcoreLexer.x
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ tokens :-

<0> "contract" {simpleToken TContract}
<0> "import" {simpleToken TImport}
<0> "export" {simpleToken TExport}
<0> "hiding" {simpleToken THiding}
<0> "as" {simpleToken TAs}
<0> "let" {simpleToken TLet}
<0> "data" {simpleToken TData}
<0> "." {simpleToken TDot}
Expand Down Expand Up @@ -165,6 +168,9 @@ data Lexeme
| TString { unStr :: String }
| TContract
| TImport
| TExport
| THiding
| TAs
| TLet
| TEq
| TDot
Expand Down Expand Up @@ -238,6 +244,9 @@ mkIdent (st, _, _, str) len
"match" -> return $ Token (position st) TMatch
"data" -> return $ Token (position st) TData
"import" -> return $ Token (position st) TImport
"export" -> return $ Token (position st) TExport
"hiding" -> return $ Token (position st) THiding
"as" -> return $ Token (position st) TAs
"contract" -> return $ Token (position st) TContract
"function" -> return $ Token (position st) TFunction
"constructor" -> return $ Token (position st) TConstructor
Expand Down
Loading
Loading