Release 1.0.0-beta.8 concrete type aliases
This commit is contained in:
parent
be6cdfb87c
commit
4f52a54bea
60
.llm/BETA_8_CONCRETE_TYPE_ALIASES.md
Normal file
60
.llm/BETA_8_CONCRETE_TYPE_ALIASES.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# 1.0.0-beta.8 Concrete Type Alias Foundation
|
||||||
|
|
||||||
|
Status: release scope for `1.0.0-beta.8`.
|
||||||
|
|
||||||
|
`1.0.0-beta.8` targets a deliberately small language-usability slice:
|
||||||
|
transparent top-level aliases for existing concrete Slovo types.
|
||||||
|
|
||||||
|
## Source Surface
|
||||||
|
|
||||||
|
The declaration form is:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type Alias TargetType)
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type JsonText string)
|
||||||
|
(type Scores (vec i32))
|
||||||
|
(type MaybeName (option string))
|
||||||
|
(type ReadResult (result string i32))
|
||||||
|
```
|
||||||
|
|
||||||
|
Aliases are module-local names. A later declaration in the same module may use
|
||||||
|
the alias wherever the resolved concrete target type is already supported.
|
||||||
|
|
||||||
|
## Resolution Contract
|
||||||
|
|
||||||
|
- Resolve aliases before typed-core lowering, checked import signatures,
|
||||||
|
backend layout, ABI decisions, and runtime behavior.
|
||||||
|
- Treat aliases as transparent names, not new nominal types.
|
||||||
|
- Preserve the target type's existing value-flow, constructor, operator,
|
||||||
|
`match`, field-access, import, and diagnostic behavior.
|
||||||
|
- Normalize exported function or struct signatures that mention local aliases
|
||||||
|
to their concrete target types for cross-module use.
|
||||||
|
- Generated documentation may show source alias spellings; runtime, checked
|
||||||
|
lowering, and cross-module typing must use the resolved concrete target.
|
||||||
|
- Reject failed alias resolution; do not silently fall back to a primitive type.
|
||||||
|
|
||||||
|
## Source Fixtures
|
||||||
|
|
||||||
|
- `lib/std/json.slo` uses local `JsonText` and `JsonField` aliases for already
|
||||||
|
encoded JSON fragments.
|
||||||
|
- `examples/projects/std-import-json/` uses the same alias shape in the
|
||||||
|
explicit standard-import fixture without importing aliases.
|
||||||
|
- `examples/projects/std-layout-local-json/` mirrors the facade and example as
|
||||||
|
a local source fixture.
|
||||||
|
|
||||||
|
These fixtures keep the existing JSON helper surface and test names. They do
|
||||||
|
not add compiler-known runtime calls or new public standard-library helper
|
||||||
|
names.
|
||||||
|
|
||||||
|
## Deferrals
|
||||||
|
|
||||||
|
This target does not add generic aliases, parameterized aliases, alias type
|
||||||
|
parameters, alias re-exports, cross-module alias imports, import aliases, glob
|
||||||
|
imports, maps/sets, alias-driven overloads, implicit casts, runtime helpers,
|
||||||
|
hosted runtime symbols, stable ABI/layout promises, or a standard-library API
|
||||||
|
freeze.
|
||||||
@ -24,15 +24,18 @@ implementation scope.
|
|||||||
networking (released in `1.0.0-beta.2` with read-only text file
|
networking (released in `1.0.0-beta.2` with read-only text file
|
||||||
handles plus narrow filesystem status and mutation calls).
|
handles plus narrow filesystem status and mutation calls).
|
||||||
3. Stabilize `lib/std` module boundaries and document beta-vs-stable APIs.
|
3. Stabilize `lib/std` module boundaries and document beta-vs-stable APIs.
|
||||||
4. Improve language usability around entry points, `match`, aliases, and
|
4. Improve language usability around entry points, `match`, concrete aliases,
|
||||||
concrete numeric completeness.
|
and concrete numeric completeness.
|
||||||
5. Expand package/workspace discipline before remote registry work.
|
5. Expand package/workspace discipline before remote registry work.
|
||||||
6. Add networking only after resource/error policy is coherent.
|
6. Add networking only after resource/error policy is coherent.
|
||||||
7. Add serialization/data-interchange helpers before richer network libraries
|
7. Add serialization/data-interchange helpers before richer network libraries
|
||||||
(released in `1.0.0-beta.7` with compact JSON text construction and JSON
|
(released in `1.0.0-beta.7` with compact JSON text construction and JSON
|
||||||
string quoting).
|
string quoting).
|
||||||
8. Design generics and collection unification from real stdlib duplication
|
8. Promote concrete type aliases before generics so long concrete vector,
|
||||||
|
option/result, array, JSON, and resource-handle signatures can be named
|
||||||
|
without changing runtime representation (released in `1.0.0-beta.8`).
|
||||||
|
9. Design generics and collection unification from real stdlib duplication
|
||||||
pressure.
|
pressure.
|
||||||
9. Add editor-facing diagnostics, watch mode, and generated documentation
|
10. Add editor-facing diagnostics, watch mode, and generated documentation
|
||||||
improvements.
|
improvements.
|
||||||
10. Freeze migration/deprecation policy and cut stable only after beta feedback.
|
11. Freeze migration/deprecation policy and cut stable only after beta feedback.
|
||||||
|
|||||||
52
.llm/reviews/BETA_8_RELEASE_REVIEW.md
Normal file
52
.llm/reviews/BETA_8_RELEASE_REVIEW.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# 1.0.0-beta.8 Release Review
|
||||||
|
|
||||||
|
Date: 2026-05-22
|
||||||
|
|
||||||
|
Scope: concrete type alias foundation.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
The Slovo documentation and source-fixture side is coherent for a beta.8
|
||||||
|
concrete-alias slice. The contract is intentionally transparent: top-level
|
||||||
|
`(type Alias TargetType)` declarations name existing supported concrete target
|
||||||
|
types, normalize before backend/ABI decisions, and do not create runtime types,
|
||||||
|
hosted symbols, generic alias machinery, maps/sets, or cross-module alias
|
||||||
|
visibility.
|
||||||
|
|
||||||
|
## Review Notes
|
||||||
|
|
||||||
|
- The language spec defines syntax, resolution, formatter shape, diagnostics,
|
||||||
|
and explicit deferrals for concrete aliases.
|
||||||
|
- The public roadmap and stable-readiness notes place beta.8 between JSON data
|
||||||
|
interchange and generics/collection unification.
|
||||||
|
- `std/json.slo` uses `JsonText` and `JsonField` as local aliases only. The
|
||||||
|
helper export list remains unchanged.
|
||||||
|
- The explicit std-import and local JSON fixtures exercise local alias use
|
||||||
|
without importing, exporting, or re-exporting aliases.
|
||||||
|
- Glagol parser, checker, formatter, project import resolution, lowering
|
||||||
|
inspectors, diagnostics fixtures, and promotion inventory now carry the
|
||||||
|
matching implementation side.
|
||||||
|
- Reviewer findings were integrated: JSON fixtures use canonical alias spacing,
|
||||||
|
unsupported alias targets are rejected, alias/value name conflicts are
|
||||||
|
diagnosed, and alias export/import attempts produce explicit visibility
|
||||||
|
diagnostics.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `cargo fmt --check`
|
||||||
|
- `cargo test --test diagnostics_contract current_negative_cases_match_machine_diagnostic_snapshots`
|
||||||
|
- `cargo test --test concrete_type_aliases_beta`
|
||||||
|
- `cargo test --test project_mode type_aliases_are_local_across_project_visibility`
|
||||||
|
- `cargo test --test lowering_inspector`
|
||||||
|
- `cargo test --test promotion_gate promotion_gate_artifacts_are_aligned`
|
||||||
|
- `cargo test --test standard_json_source_facade_alpha`
|
||||||
|
- `cargo test`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
## Remaining Deferred Work
|
||||||
|
|
||||||
|
- Generated documentation may still display source alias spelling. Runtime
|
||||||
|
behavior, checked lowering, backend layout, and cross-module typing use the
|
||||||
|
resolved concrete target.
|
||||||
|
- Any future generic alias, cross-module alias import/export, or re-export
|
||||||
|
design.
|
||||||
30
README.md
30
README.md
@ -6,7 +6,7 @@ This repository is the canonical public monorepo for the language design,
|
|||||||
standard library source, compiler, runtime, examples, benchmarks, and technical
|
standard library source, compiler, runtime, examples, benchmarks, and technical
|
||||||
documents.
|
documents.
|
||||||
|
|
||||||
Current release: `1.0.0-beta.7`.
|
Current release: `1.0.0-beta.8`.
|
||||||
|
|
||||||
## Repository Layout
|
## Repository Layout
|
||||||
|
|
||||||
@ -24,20 +24,22 @@ scripts/ local release and document tooling
|
|||||||
|
|
||||||
## Beta Scope
|
## Beta Scope
|
||||||
|
|
||||||
`1.0.0-beta.7` keeps the `1.0.0-beta` language baseline, includes the
|
`1.0.0-beta.8` keeps the `1.0.0-beta` language baseline, includes the
|
||||||
`1.0.0-beta.1` tooling/install hardening slice, the `1.0.0-beta.2`
|
`1.0.0-beta.1` tooling/install hardening slice, the `1.0.0-beta.2`
|
||||||
runtime/resource foundation bundle, the `1.0.0-beta.3` standard-library
|
runtime/resource foundation bundle, the `1.0.0-beta.3` standard-library
|
||||||
stabilization bundle, the `1.0.0-beta.4` language-usability diagnostics
|
stabilization bundle, the `1.0.0-beta.4` language-usability diagnostics
|
||||||
bundle, the `1.0.0-beta.5` local package/workspace discipline bundle, and the
|
bundle, the `1.0.0-beta.5` local package/workspace discipline bundle, and the
|
||||||
`1.0.0-beta.6` loopback networking foundation, plus the `1.0.0-beta.7`
|
`1.0.0-beta.6` loopback networking foundation, plus the `1.0.0-beta.7`
|
||||||
serialization/data-interchange foundation. The language baseline supports
|
serialization/data-interchange foundation and the `1.0.0-beta.8` concrete type
|
||||||
practical local command-line, file, and loopback-network programs with:
|
alias foundation. The language baseline supports practical local command-line,
|
||||||
|
file, and loopback-network programs with:
|
||||||
|
|
||||||
- modules, explicit imports, packages, and local workspaces
|
- modules, explicit imports, packages, and local workspaces
|
||||||
- `new`, `check`, `fmt`, `test`, `doc`, and `build`
|
- `new`, `check`, `fmt`, `test`, `doc`, and `build`
|
||||||
- `i32`, `i64`, `u32`, `u64`, `f64`, `bool`, `string`, and internal `unit`
|
- `i32`, `i64`, `u32`, `u64`, `f64`, `bool`, `string`, and internal `unit`
|
||||||
- structs, enums, fixed arrays, concrete vectors, option/result families, and
|
- structs, enums, fixed arrays, concrete vectors, option/result families, and
|
||||||
current `match`
|
current `match`
|
||||||
|
- module-local transparent concrete type aliases
|
||||||
- explicit `std/*.slo` imports from `lib/std`, installed `share/slovo/std`, or
|
- explicit `std/*.slo` imports from `lib/std`, installed `share/slovo/std`, or
|
||||||
`SLOVO_STD_PATH`
|
`SLOVO_STD_PATH`
|
||||||
- beta-scoped loopback TCP handles through `std.net`
|
- beta-scoped loopback TCP handles through `std.net`
|
||||||
@ -48,6 +50,11 @@ Still deferred before stable: generics, maps/sets, broad package registry
|
|||||||
semantics, DNS/TLS/async networking, LSP/watch/debug-adapter guarantees,
|
semantics, DNS/TLS/async networking, LSP/watch/debug-adapter guarantees,
|
||||||
stable ABI and layout, and a stable standard-library compatibility freeze.
|
stable ABI and layout, and a stable standard-library compatibility freeze.
|
||||||
|
|
||||||
|
The next likely language slice is `1.0.0-beta.9`, focused on the first
|
||||||
|
generics and collection-unification design pressure. It should build on the
|
||||||
|
alias foundation without adding maps/sets or standard-library API freeze claims
|
||||||
|
until the contract and gates are explicit.
|
||||||
|
|
||||||
## Build And Test
|
## Build And Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -194,6 +201,21 @@ This is not a complete JSON library. Full parsing, recursive JSON values,
|
|||||||
maps/sets, streaming encoders, schema validation, Unicode normalization, and a
|
maps/sets, streaming encoders, schema validation, Unicode normalization, and a
|
||||||
stable data-interchange API freeze remain deferred.
|
stable data-interchange API freeze remain deferred.
|
||||||
|
|
||||||
|
## 1.0.0-beta.8 Concrete Type Alias Foundation
|
||||||
|
|
||||||
|
The `1.0.0-beta.8` release adds transparent concrete type aliases:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type JsonText string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Aliases are module-local names for already supported concrete target types.
|
||||||
|
They may appear in local signatures and annotations, but they do not create new
|
||||||
|
runtime representations or stable ABI names. Project imports of functions that
|
||||||
|
use aliases see the resolved concrete target type. Alias exports, imports,
|
||||||
|
re-exports, generic aliases, parameterized aliases, maps/sets, and new
|
||||||
|
compiler-known runtime names remain out of scope.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Language Manifest](docs/language/MANIFEST.md)
|
- [Language Manifest](docs/language/MANIFEST.md)
|
||||||
|
|||||||
2
compiler/Cargo.lock
generated
2
compiler/Cargo.lock
generated
@ -4,4 +4,4 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glagol"
|
name = "glagol"
|
||||||
version = "1.0.0-beta.7"
|
version = "1.0.0-beta.8"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "glagol"
|
name = "glagol"
|
||||||
version = "1.0.0-beta.7"
|
version = "1.0.0-beta.8"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Glagol, the first compiler for the Slovo language"
|
description = "Glagol, the first compiler for the Slovo language"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use crate::{token::Span, types::Type};
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub module: String,
|
pub module: String,
|
||||||
|
pub type_aliases: Vec<TypeAliasDecl>,
|
||||||
pub enums: Vec<EnumDecl>,
|
pub enums: Vec<EnumDecl>,
|
||||||
pub structs: Vec<StructDecl>,
|
pub structs: Vec<StructDecl>,
|
||||||
pub c_imports: Vec<CImportDecl>,
|
pub c_imports: Vec<CImportDecl>,
|
||||||
@ -10,6 +11,15 @@ pub struct Program {
|
|||||||
pub tests: Vec<Test>,
|
pub tests: Vec<Test>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypeAliasDecl {
|
||||||
|
pub name: String,
|
||||||
|
pub name_span: Span,
|
||||||
|
pub target: Type,
|
||||||
|
pub target_span: Span,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EnumDecl {
|
pub struct EnumDecl {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use std::collections::{hash_map::Entry, HashMap, HashSet};
|
use std::collections::{hash_map::Entry, BTreeSet, HashMap, HashSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
BinaryOp, EnumDecl, Expr, ExprKind, Function, MatchArm, MatchPatternKind, Program,
|
BinaryOp, EnumDecl, Expr, ExprKind, Function, MatchArm, MatchPatternKind, Program,
|
||||||
StructDecl, StructInitField, Test,
|
StructDecl, StructInitField, Test, TypeAliasDecl,
|
||||||
},
|
},
|
||||||
diag::Diagnostic,
|
diag::Diagnostic,
|
||||||
lower, std_runtime,
|
lower, std_runtime,
|
||||||
@ -294,13 +294,23 @@ pub fn check_program_with_imports(
|
|||||||
|
|
||||||
fn check_program_inner(
|
fn check_program_inner(
|
||||||
file: &str,
|
file: &str,
|
||||||
program: Program,
|
mut program: Program,
|
||||||
external_functions: &[ExternalFunction],
|
external_functions: &[ExternalFunction],
|
||||||
external_structs: &[ExternalStruct],
|
external_structs: &[ExternalStruct],
|
||||||
external_enums: &[ExternalEnum],
|
external_enums: &[ExternalEnum],
|
||||||
project_resolution: bool,
|
project_resolution: bool,
|
||||||
) -> Result<CheckedProgram, Vec<Diagnostic>> {
|
) -> Result<CheckedProgram, Vec<Diagnostic>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
errors.extend(resolve_type_aliases(
|
||||||
|
file,
|
||||||
|
&mut program,
|
||||||
|
external_structs,
|
||||||
|
external_enums,
|
||||||
|
));
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
|
|
||||||
let mut functions = HashMap::new();
|
let mut functions = HashMap::new();
|
||||||
let mut structs = HashMap::new();
|
let mut structs = HashMap::new();
|
||||||
let mut enums = HashMap::new();
|
let mut enums = HashMap::new();
|
||||||
@ -580,6 +590,641 @@ fn is_reserved_callable_name(name: &str) -> bool {
|
|||||||
std_runtime::is_reserved_name(name) || unsafe_ops::is_reserved_head(name)
|
std_runtime::is_reserved_name(name) || unsafe_ops::is_reserved_head(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_type_aliases(
|
||||||
|
file: &str,
|
||||||
|
program: &mut Program,
|
||||||
|
external_structs: &[ExternalStruct],
|
||||||
|
external_enums: &[ExternalEnum],
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
if program.type_aliases.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut struct_names = HashMap::new();
|
||||||
|
for struct_decl in &program.structs {
|
||||||
|
struct_names
|
||||||
|
.entry(struct_decl.name.clone())
|
||||||
|
.or_insert(struct_decl.name_span);
|
||||||
|
}
|
||||||
|
for struct_decl in external_structs {
|
||||||
|
struct_names
|
||||||
|
.entry(struct_decl.name.clone())
|
||||||
|
.or_insert(struct_decl.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut enum_names = HashMap::new();
|
||||||
|
for enum_decl in &program.enums {
|
||||||
|
enum_names
|
||||||
|
.entry(enum_decl.name.clone())
|
||||||
|
.or_insert(enum_decl.name_span);
|
||||||
|
}
|
||||||
|
for enum_decl in external_enums {
|
||||||
|
enum_names
|
||||||
|
.entry(enum_decl.name.clone())
|
||||||
|
.or_insert(enum_decl.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut function_names = HashMap::new();
|
||||||
|
for function in &program.functions {
|
||||||
|
function_names
|
||||||
|
.entry(function.name.clone())
|
||||||
|
.or_insert(function.span);
|
||||||
|
}
|
||||||
|
let mut c_import_names = HashMap::new();
|
||||||
|
for import in &program.c_imports {
|
||||||
|
c_import_names
|
||||||
|
.entry(import.name.clone())
|
||||||
|
.or_insert(import.name_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut aliases = HashMap::<String, &TypeAliasDecl>::new();
|
||||||
|
for alias in &program.type_aliases {
|
||||||
|
match aliases.entry(alias.name.clone()) {
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(alias);
|
||||||
|
}
|
||||||
|
Entry::Occupied(entry) => errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"DuplicateTypeAlias",
|
||||||
|
format!("duplicate type alias `{}`", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("original type alias", entry.get().name_span),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for alias in &program.type_aliases {
|
||||||
|
if is_reserved_type_name(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!(
|
||||||
|
"type alias `{}` conflicts with a built-in type name",
|
||||||
|
alias.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.hint("choose an alias name distinct from built-in type names"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(span) = struct_names.get(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with a struct", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("struct declaration", *span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(span) = enum_names.get(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with an enum", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("enum declaration", *span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(span) = function_names.get(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with a function", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("function declaration", *span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(span) = c_import_names.get(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with a C import", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("C import declaration", *span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if matches!(&alias.target, Type::Named(name) if name == &alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"SelfTypeAlias",
|
||||||
|
format!("type alias `{}` directly aliases itself", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.target_span)
|
||||||
|
.hint("point the alias at an existing concrete type"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
let known_structs = struct_names.keys().cloned().collect::<HashSet<_>>();
|
||||||
|
let known_enums = enum_names.keys().cloned().collect::<HashSet<_>>();
|
||||||
|
let mut resolved = HashMap::new();
|
||||||
|
let mut failed = HashSet::new();
|
||||||
|
let mut reported_cycles = BTreeSet::new();
|
||||||
|
let mut names = aliases.keys().cloned().collect::<Vec<_>>();
|
||||||
|
names.sort();
|
||||||
|
for name in names {
|
||||||
|
resolve_alias_target(
|
||||||
|
file,
|
||||||
|
&name,
|
||||||
|
&aliases,
|
||||||
|
&known_structs,
|
||||||
|
&known_enums,
|
||||||
|
&mut resolved,
|
||||||
|
&mut failed,
|
||||||
|
&mut Vec::new(),
|
||||||
|
&mut reported_cycles,
|
||||||
|
&mut errors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite_program_alias_types(program, &resolved);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_alias_target(
|
||||||
|
file: &str,
|
||||||
|
name: &str,
|
||||||
|
aliases: &HashMap<String, &TypeAliasDecl>,
|
||||||
|
structs: &HashSet<String>,
|
||||||
|
enums: &HashSet<String>,
|
||||||
|
resolved: &mut HashMap<String, Type>,
|
||||||
|
failed: &mut HashSet<String>,
|
||||||
|
stack: &mut Vec<String>,
|
||||||
|
reported_cycles: &mut BTreeSet<String>,
|
||||||
|
errors: &mut Vec<Diagnostic>,
|
||||||
|
) -> Option<Type> {
|
||||||
|
if let Some(ty) = resolved.get(name) {
|
||||||
|
return Some(ty.clone());
|
||||||
|
}
|
||||||
|
if failed.contains(name) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(cycle_start) = stack.iter().position(|candidate| candidate == name) {
|
||||||
|
let cycle = stack[cycle_start..].to_vec();
|
||||||
|
let mut key = cycle.clone();
|
||||||
|
key.sort();
|
||||||
|
if reported_cycles.insert(key.join("|")) {
|
||||||
|
errors.push(type_alias_cycle(file, &cycle, aliases));
|
||||||
|
}
|
||||||
|
failed.extend(cycle);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias = aliases.get(name).copied()?;
|
||||||
|
stack.push(name.to_string());
|
||||||
|
let target = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
&alias.target,
|
||||||
|
alias.target_span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
|
stack.pop();
|
||||||
|
|
||||||
|
if let Some(target) = target {
|
||||||
|
resolved.insert(name.to_string(), target.clone());
|
||||||
|
Some(target)
|
||||||
|
} else {
|
||||||
|
failed.insert(name.to_string());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_alias_target_type(
|
||||||
|
file: &str,
|
||||||
|
ty: &Type,
|
||||||
|
span: Span,
|
||||||
|
aliases: &HashMap<String, &TypeAliasDecl>,
|
||||||
|
structs: &HashSet<String>,
|
||||||
|
enums: &HashSet<String>,
|
||||||
|
resolved: &mut HashMap<String, Type>,
|
||||||
|
failed: &mut HashSet<String>,
|
||||||
|
stack: &mut Vec<String>,
|
||||||
|
reported_cycles: &mut BTreeSet<String>,
|
||||||
|
errors: &mut Vec<Diagnostic>,
|
||||||
|
) -> Option<Type> {
|
||||||
|
match ty {
|
||||||
|
Type::Named(name) if aliases.contains_key(name) => resolve_alias_target(
|
||||||
|
file,
|
||||||
|
name,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
),
|
||||||
|
Type::Named(name) if structs.contains(name) || enums.contains(name) => {
|
||||||
|
Some(Type::Named(name.clone()))
|
||||||
|
}
|
||||||
|
Type::Named(name) => {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"UnknownTypeAliasTarget",
|
||||||
|
format!("type alias target `{}` is not a known concrete type", name),
|
||||||
|
)
|
||||||
|
.with_span(span)
|
||||||
|
.expected("built-in type, known struct, known enum, or known type alias")
|
||||||
|
.found(name.clone())
|
||||||
|
.hint("declare the target type or alias before checking this alias set"),
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Type::Array(inner, len) => {
|
||||||
|
let resolved_inner = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
inner,
|
||||||
|
span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
)?;
|
||||||
|
let target = Type::Array(Box::new(resolved_inner), *len);
|
||||||
|
if alias_target_type_supported(&target, structs, enums) {
|
||||||
|
Some(target)
|
||||||
|
} else {
|
||||||
|
errors.push(unsupported_type_alias_target(file, span, &target));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Vec(inner) => {
|
||||||
|
let resolved_inner = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
inner,
|
||||||
|
span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
)?;
|
||||||
|
let target = Type::Vec(Box::new(resolved_inner));
|
||||||
|
if alias_target_type_supported(&target, structs, enums) {
|
||||||
|
Some(target)
|
||||||
|
} else {
|
||||||
|
errors.push(unsupported_type_alias_target(file, span, &target));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Option(inner) => {
|
||||||
|
let resolved_inner = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
inner,
|
||||||
|
span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
)?;
|
||||||
|
let target = Type::Option(Box::new(resolved_inner));
|
||||||
|
if alias_target_type_supported(&target, structs, enums) {
|
||||||
|
Some(target)
|
||||||
|
} else {
|
||||||
|
errors.push(unsupported_type_alias_target(file, span, &target));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Result(ok, err) => {
|
||||||
|
let resolved_ok = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
ok,
|
||||||
|
span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
)?;
|
||||||
|
let resolved_err = resolve_alias_target_type(
|
||||||
|
file,
|
||||||
|
err,
|
||||||
|
span,
|
||||||
|
aliases,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
resolved,
|
||||||
|
failed,
|
||||||
|
stack,
|
||||||
|
reported_cycles,
|
||||||
|
errors,
|
||||||
|
)?;
|
||||||
|
let target = Type::Result(Box::new(resolved_ok), Box::new(resolved_err));
|
||||||
|
if alias_target_type_supported(&target, structs, enums) {
|
||||||
|
Some(target)
|
||||||
|
} else {
|
||||||
|
errors.push(unsupported_type_alias_target(file, span, &target));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if alias_target_type_supported(ty, structs, enums) {
|
||||||
|
Some(ty.clone())
|
||||||
|
} else {
|
||||||
|
errors.push(unsupported_type_alias_target(file, span, ty));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alias_target_type_supported(
|
||||||
|
ty: &Type,
|
||||||
|
structs: &HashSet<String>,
|
||||||
|
enums: &HashSet<String>,
|
||||||
|
) -> bool {
|
||||||
|
match ty {
|
||||||
|
Type::I32 | Type::I64 | Type::U32 | Type::U64 | Type::F64 | Type::Bool | Type::String => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Type::Named(name) => structs.contains(name) || enums.contains(name),
|
||||||
|
Type::Array(inner, len) => {
|
||||||
|
*len > 0
|
||||||
|
&& (matches!(
|
||||||
|
&**inner,
|
||||||
|
Type::I32
|
||||||
|
| Type::I64
|
||||||
|
| Type::U32
|
||||||
|
| Type::U64
|
||||||
|
| Type::F64
|
||||||
|
| Type::Bool
|
||||||
|
| Type::String
|
||||||
|
) || matches!(&**inner, Type::Named(name) if structs.contains(name) || enums.contains(name)))
|
||||||
|
}
|
||||||
|
Type::Vec(inner) => matches!(
|
||||||
|
&**inner,
|
||||||
|
Type::I32 | Type::I64 | Type::F64 | Type::Bool | Type::String
|
||||||
|
),
|
||||||
|
Type::Option(inner) => option_payload_type_supported(inner),
|
||||||
|
Type::Result(ok, err) => result_payload_types_supported(ok, err),
|
||||||
|
Type::Unit | Type::Ptr(_) | Type::Slice(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unsupported_type_alias_target(file: &str, span: Span, ty: &Type) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"UnsupportedTypeAliasTarget",
|
||||||
|
"type alias target type is not supported in beta.8",
|
||||||
|
)
|
||||||
|
.with_span(span)
|
||||||
|
.expected("i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
.found(ty.to_string())
|
||||||
|
.hint("aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_alias_cycle(
|
||||||
|
file: &str,
|
||||||
|
cycle: &[String],
|
||||||
|
aliases: &HashMap<String, &TypeAliasDecl>,
|
||||||
|
) -> Diagnostic {
|
||||||
|
let first = &cycle[0];
|
||||||
|
let first_alias = aliases[first];
|
||||||
|
let mut diag = Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasCycle",
|
||||||
|
format!("type alias cycle includes `{}`", first),
|
||||||
|
)
|
||||||
|
.with_span(first_alias.name_span)
|
||||||
|
.hint("type aliases must resolve to an existing non-alias concrete type");
|
||||||
|
|
||||||
|
for name in &cycle[1..] {
|
||||||
|
diag = diag.related(
|
||||||
|
format!("cycle also includes `{}`", name),
|
||||||
|
aliases[name].name_span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
diag
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_program_alias_types(program: &mut Program, aliases: &HashMap<String, Type>) {
|
||||||
|
for enum_decl in &mut program.enums {
|
||||||
|
for variant in &mut enum_decl.variants {
|
||||||
|
if let Some(payload_ty) = &mut variant.payload_ty {
|
||||||
|
*payload_ty = resolve_use_type(payload_ty, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for struct_decl in &mut program.structs {
|
||||||
|
for field in &mut struct_decl.fields {
|
||||||
|
field.ty = resolve_use_type(&field.ty, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for import in &mut program.c_imports {
|
||||||
|
for param in &mut import.params {
|
||||||
|
param.ty = resolve_use_type(¶m.ty, aliases);
|
||||||
|
}
|
||||||
|
import.return_type = resolve_use_type(&import.return_type, aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in &mut program.functions {
|
||||||
|
for param in &mut function.params {
|
||||||
|
param.ty = resolve_use_type(¶m.ty, aliases);
|
||||||
|
}
|
||||||
|
function.return_type = resolve_use_type(&function.return_type, aliases);
|
||||||
|
for expr in &mut function.body {
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for test in &mut program.tests {
|
||||||
|
for expr in &mut test.body {
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_expr_alias_types(expr: &mut Expr, aliases: &HashMap<String, Type>) {
|
||||||
|
match &mut expr.kind {
|
||||||
|
ExprKind::StructInit { fields, .. } => {
|
||||||
|
for field in fields {
|
||||||
|
rewrite_expr_alias_types(&mut field.expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::ArrayInit {
|
||||||
|
elem_ty, elements, ..
|
||||||
|
} => {
|
||||||
|
*elem_ty = resolve_use_type(elem_ty, aliases);
|
||||||
|
for element in elements {
|
||||||
|
rewrite_expr_alias_types(element, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::OptionSome {
|
||||||
|
payload_ty, value, ..
|
||||||
|
} => {
|
||||||
|
*payload_ty = resolve_use_type(payload_ty, aliases);
|
||||||
|
rewrite_expr_alias_types(value, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::OptionNone { payload_ty, .. } => {
|
||||||
|
*payload_ty = resolve_use_type(payload_ty, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::ResultOk {
|
||||||
|
ok_ty,
|
||||||
|
err_ty,
|
||||||
|
value,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| ExprKind::ResultErr {
|
||||||
|
ok_ty,
|
||||||
|
err_ty,
|
||||||
|
value,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*ok_ty = resolve_use_type(ok_ty, aliases);
|
||||||
|
*err_ty = resolve_use_type(err_ty, aliases);
|
||||||
|
rewrite_expr_alias_types(value, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::OptionIsSome { value }
|
||||||
|
| ExprKind::OptionIsNone { value }
|
||||||
|
| ExprKind::OptionUnwrapSome { value }
|
||||||
|
| ExprKind::ResultIsOk { value, .. }
|
||||||
|
| ExprKind::ResultIsErr { value, .. }
|
||||||
|
| ExprKind::ResultUnwrapOk { value, .. }
|
||||||
|
| ExprKind::ResultUnwrapErr { value, .. } => rewrite_expr_alias_types(value, aliases),
|
||||||
|
ExprKind::EnumVariant { args, .. } | ExprKind::Call { args, .. } => {
|
||||||
|
for arg in args {
|
||||||
|
rewrite_expr_alias_types(arg, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::FieldAccess { value, .. } => rewrite_expr_alias_types(value, aliases),
|
||||||
|
ExprKind::Index { array, index } => {
|
||||||
|
rewrite_expr_alias_types(array, aliases);
|
||||||
|
rewrite_expr_alias_types(index, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::Local { ty, expr, .. } => {
|
||||||
|
*ty = resolve_use_type(ty, aliases);
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::Set { expr, .. } => rewrite_expr_alias_types(expr, aliases),
|
||||||
|
ExprKind::Binary { left, right, .. } => {
|
||||||
|
rewrite_expr_alias_types(left, aliases);
|
||||||
|
rewrite_expr_alias_types(right, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::If {
|
||||||
|
condition,
|
||||||
|
then_expr,
|
||||||
|
else_expr,
|
||||||
|
} => {
|
||||||
|
rewrite_expr_alias_types(condition, aliases);
|
||||||
|
rewrite_expr_alias_types(then_expr, aliases);
|
||||||
|
rewrite_expr_alias_types(else_expr, aliases);
|
||||||
|
}
|
||||||
|
ExprKind::Match { subject, arms } => {
|
||||||
|
rewrite_expr_alias_types(subject, aliases);
|
||||||
|
for arm in arms {
|
||||||
|
for expr in &mut arm.body {
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::While { condition, body } => {
|
||||||
|
rewrite_expr_alias_types(condition, aliases);
|
||||||
|
for expr in body {
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Unsafe { body } => {
|
||||||
|
for expr in body {
|
||||||
|
rewrite_expr_alias_types(expr, aliases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Int(_)
|
||||||
|
| ExprKind::Int64(_)
|
||||||
|
| ExprKind::UInt32(_)
|
||||||
|
| ExprKind::UInt64(_)
|
||||||
|
| ExprKind::Float(_)
|
||||||
|
| ExprKind::Bool(_)
|
||||||
|
| ExprKind::String(_)
|
||||||
|
| ExprKind::Var(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_use_type(ty: &Type, aliases: &HashMap<String, Type>) -> Type {
|
||||||
|
match ty {
|
||||||
|
Type::Named(name) => aliases
|
||||||
|
.get(name)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| Type::Named(name.clone())),
|
||||||
|
Type::Ptr(inner) => Type::Ptr(Box::new(resolve_use_type(inner, aliases))),
|
||||||
|
Type::Array(inner, len) => Type::Array(Box::new(resolve_use_type(inner, aliases)), *len),
|
||||||
|
Type::Vec(inner) => Type::Vec(Box::new(resolve_use_type(inner, aliases))),
|
||||||
|
Type::Slice(inner) => Type::Slice(Box::new(resolve_use_type(inner, aliases))),
|
||||||
|
Type::Option(inner) => Type::Option(Box::new(resolve_use_type(inner, aliases))),
|
||||||
|
Type::Result(ok, err) => Type::Result(
|
||||||
|
Box::new(resolve_use_type(ok, aliases)),
|
||||||
|
Box::new(resolve_use_type(err, aliases)),
|
||||||
|
),
|
||||||
|
_ => ty.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_reserved_type_name(name: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
name,
|
||||||
|
"i32"
|
||||||
|
| "i64"
|
||||||
|
| "u32"
|
||||||
|
| "u64"
|
||||||
|
| "f64"
|
||||||
|
| "bool"
|
||||||
|
| "unit"
|
||||||
|
| "string"
|
||||||
|
| "ptr"
|
||||||
|
| "slice"
|
||||||
|
| "option"
|
||||||
|
| "result"
|
||||||
|
| "array"
|
||||||
|
| "vec"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_struct_decl(
|
fn check_struct_decl(
|
||||||
file: &str,
|
file: &str,
|
||||||
struct_decl: &StructDecl,
|
struct_decl: &StructDecl,
|
||||||
|
|||||||
@ -19,6 +19,7 @@ pub fn format(file: &str, source: &str, forms: &[SExpr]) -> Result<String, Vec<D
|
|||||||
function_names: collect_function_names(forms),
|
function_names: collect_function_names(forms),
|
||||||
struct_names: collect_struct_names(forms),
|
struct_names: collect_struct_names(forms),
|
||||||
enum_names: collect_enum_names(forms),
|
enum_names: collect_enum_names(forms),
|
||||||
|
type_alias_names: collect_type_alias_names(forms),
|
||||||
errors: comment_errors
|
errors: comment_errors
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|comment| unsupported_non_full_line_comment(file, comment.span))
|
.map(|comment| unsupported_non_full_line_comment(file, comment.span))
|
||||||
@ -42,6 +43,7 @@ struct Formatter<'a> {
|
|||||||
function_names: HashSet<String>,
|
function_names: HashSet<String>,
|
||||||
struct_names: HashSet<String>,
|
struct_names: HashSet<String>,
|
||||||
enum_names: HashSet<String>,
|
enum_names: HashSet<String>,
|
||||||
|
type_alias_names: HashSet<String>,
|
||||||
errors: Vec<Diagnostic>,
|
errors: Vec<Diagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +66,7 @@ impl Formatter<'_> {
|
|||||||
Some("module") => self.write_module(form),
|
Some("module") => self.write_module(form),
|
||||||
Some("import") => self.write_import(form),
|
Some("import") => self.write_import(form),
|
||||||
Some("import_c") => self.write_c_import(form),
|
Some("import_c") => self.write_c_import(form),
|
||||||
|
Some("type") => self.write_type_alias(form),
|
||||||
Some("enum") => self.write_enum(form),
|
Some("enum") => self.write_enum(form),
|
||||||
Some("struct") => self.write_struct(form),
|
Some("struct") => self.write_struct(form),
|
||||||
Some("fn") => self.write_function(form),
|
Some("fn") => self.write_function(form),
|
||||||
@ -251,6 +254,59 @@ impl Formatter<'_> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_type_alias(&mut self, form: &SExpr) {
|
||||||
|
let Some(items) = expect_list(form) else {
|
||||||
|
self.errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
self.file,
|
||||||
|
"MalformedTypeAlias",
|
||||||
|
"type alias form must be a list",
|
||||||
|
)
|
||||||
|
.with_span(form.span),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if items.len() != 3 {
|
||||||
|
self.errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
self.file,
|
||||||
|
"MalformedTypeAlias",
|
||||||
|
"type alias form must be `(type Alias TargetType)`",
|
||||||
|
)
|
||||||
|
.with_span(form.span),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(name) = expect_ident(&items[1]) else {
|
||||||
|
self.errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
self.file,
|
||||||
|
"InvalidTypeAliasName",
|
||||||
|
"type alias name must be an identifier",
|
||||||
|
)
|
||||||
|
.with_span(items[1].span),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(target) = self.render_alias_target_type(&items[2]) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.reject_comments_before(
|
||||||
|
form.span.end,
|
||||||
|
"formatter does not support comments inside type alias forms",
|
||||||
|
);
|
||||||
|
|
||||||
|
self.output.push_str("(type ");
|
||||||
|
self.output.push_str(name);
|
||||||
|
self.output.push(' ');
|
||||||
|
self.output.push_str(&target);
|
||||||
|
self.output.push(')');
|
||||||
|
}
|
||||||
|
|
||||||
fn write_struct(&mut self, form: &SExpr) {
|
fn write_struct(&mut self, form: &SExpr) {
|
||||||
let Some(items) = expect_list(form) else {
|
let Some(items) = expect_list(form) else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
@ -512,17 +568,17 @@ impl Formatter<'_> {
|
|||||||
} else if is_ident(&variant_items[1], "string") {
|
} else if is_ident(&variant_items[1], "string") {
|
||||||
"string".to_string()
|
"string".to_string()
|
||||||
} else if let Some(name) = expect_ident(&variant_items[1]) {
|
} else if let Some(name) = expect_ident(&variant_items[1]) {
|
||||||
if self.struct_names.contains(name) {
|
if self.struct_names.contains(name) || self.type_alias_names.contains(name) {
|
||||||
name.to_string()
|
name.to_string()
|
||||||
} else {
|
} else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
self.file,
|
self.file,
|
||||||
"UnsupportedFormatterForm",
|
"UnsupportedFormatterForm",
|
||||||
"formatter supports only unary direct i32, i64, f64, bool, string, and known non-recursive struct enum payload variants",
|
"formatter supports only unary direct i32, i64, f64, bool, string, known type aliases, and known non-recursive struct enum payload variants",
|
||||||
)
|
)
|
||||||
.with_span(variant_items[1].span)
|
.with_span(variant_items[1].span)
|
||||||
.expected("i32, i64, f64, bool, string, or known non-recursive struct type"),
|
.expected("i32, i64, f64, bool, string, known type alias, or known non-recursive struct type"),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -531,10 +587,10 @@ impl Formatter<'_> {
|
|||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
self.file,
|
self.file,
|
||||||
"UnsupportedFormatterForm",
|
"UnsupportedFormatterForm",
|
||||||
"formatter supports only unary direct i32, i64, f64, bool, string, and known non-recursive struct enum payload variants",
|
"formatter supports only unary direct i32, i64, f64, bool, string, known type aliases, and known non-recursive struct enum payload variants",
|
||||||
)
|
)
|
||||||
.with_span(variant_items[1].span)
|
.with_span(variant_items[1].span)
|
||||||
.expected("i32, i64, f64, bool, string, or known non-recursive struct type"),
|
.expected("i32, i64, f64, bool, string, known type alias, or known non-recursive struct type"),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -847,7 +903,10 @@ impl Formatter<'_> {
|
|||||||
} else if is_ident(&pair[1], "string") {
|
} else if is_ident(&pair[1], "string") {
|
||||||
"string".to_string()
|
"string".to_string()
|
||||||
} else if let Some(name) = expect_ident(&pair[1]) {
|
} else if let Some(name) = expect_ident(&pair[1]) {
|
||||||
if self.struct_names.contains(name) || self.enum_names.contains(name) {
|
if self.struct_names.contains(name)
|
||||||
|
|| self.enum_names.contains(name)
|
||||||
|
|| self.type_alias_names.contains(name)
|
||||||
|
{
|
||||||
name.to_string()
|
name.to_string()
|
||||||
} else {
|
} else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
@ -861,14 +920,18 @@ impl Formatter<'_> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if let Some(items) = expect_list(&pair[1]) {
|
} else if let Some(items) = expect_list(&pair[1]) {
|
||||||
if let Some(text) = render_option_result_type(items) {
|
if let Some(text) = render_option_result_type(items, &self.type_alias_names) {
|
||||||
text
|
text
|
||||||
} else if let Some(text) =
|
} else if let Some(text) = render_supported_array_type(
|
||||||
render_supported_array_type(items, &self.struct_names, &self.enum_names)
|
items,
|
||||||
|
&self.struct_names,
|
||||||
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
|
) {
|
||||||
|
text
|
||||||
|
} else if let Some(text) = render_supported_vec_type(items, &self.type_alias_names)
|
||||||
{
|
{
|
||||||
text
|
text
|
||||||
} else if let Some(text) = render_supported_vec_type(items) {
|
|
||||||
text
|
|
||||||
} else {
|
} else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
@ -946,7 +1009,10 @@ impl Formatter<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = expect_ident(form) {
|
if let Some(name) = expect_ident(form) {
|
||||||
if self.struct_names.contains(name) || self.enum_names.contains(name) {
|
if self.struct_names.contains(name)
|
||||||
|
|| self.enum_names.contains(name)
|
||||||
|
|| self.type_alias_names.contains(name)
|
||||||
|
{
|
||||||
return Some(name.to_string());
|
return Some(name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -963,16 +1029,20 @@ impl Formatter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(text) = render_option_result_type(items) {
|
if let Some(text) = render_option_result_type(items, &self.type_alias_names) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = render_supported_array_type(items, &self.struct_names, &self.enum_names)
|
if let Some(text) = render_supported_array_type(
|
||||||
{
|
items,
|
||||||
|
&self.struct_names,
|
||||||
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
|
) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = render_supported_vec_type(items) {
|
if let Some(text) = render_supported_vec_type(items, &self.type_alias_names) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -987,6 +1057,58 @@ impl Formatter<'_> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_alias_target_type(&mut self, form: &SExpr) -> Option<String> {
|
||||||
|
if let Some(text) = render_direct_type_atom(
|
||||||
|
form,
|
||||||
|
&self.struct_names,
|
||||||
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
|
) {
|
||||||
|
return Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(items) = expect_list(form) else {
|
||||||
|
self.errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
self.file,
|
||||||
|
"InvalidTypeAliasTarget",
|
||||||
|
"type alias target type is invalid",
|
||||||
|
)
|
||||||
|
.with_span(form.span)
|
||||||
|
.expected("concrete type"),
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(text) = render_option_result_type(items, &self.type_alias_names) {
|
||||||
|
return Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = render_supported_array_type(
|
||||||
|
items,
|
||||||
|
&self.struct_names,
|
||||||
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
|
) {
|
||||||
|
return Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = render_supported_vec_type(items, &self.type_alias_names) {
|
||||||
|
return Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
self.file,
|
||||||
|
"InvalidTypeAliasTarget",
|
||||||
|
"type alias target type is invalid",
|
||||||
|
)
|
||||||
|
.with_span(form.span)
|
||||||
|
.expected("concrete type"),
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn render_c_import_return_type(&mut self, form: &SExpr) -> Option<String> {
|
fn render_c_import_return_type(&mut self, form: &SExpr) -> Option<String> {
|
||||||
if is_ident(form, "i32") {
|
if is_ident(form, "i32") {
|
||||||
return Some("i32".to_string());
|
return Some("i32".to_string());
|
||||||
@ -1318,6 +1440,12 @@ impl Formatter<'_> {
|
|||||||
is_array: false,
|
is_array: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if self.type_alias_names.contains(name) {
|
||||||
|
return Some(RenderedType {
|
||||||
|
text: name.to_string(),
|
||||||
|
is_array: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(items) = expect_list(form) else {
|
let Some(items) = expect_list(form) else {
|
||||||
@ -1332,14 +1460,14 @@ impl Formatter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(text) = render_option_result_type(items) {
|
if let Some(text) = render_option_result_type(items, &self.type_alias_names) {
|
||||||
return Some(RenderedType {
|
return Some(RenderedType {
|
||||||
text,
|
text,
|
||||||
is_array: false,
|
is_array: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = render_supported_vec_type(items) {
|
if let Some(text) = render_supported_vec_type(items, &self.type_alias_names) {
|
||||||
return Some(RenderedType {
|
return Some(RenderedType {
|
||||||
text,
|
text,
|
||||||
is_array: false,
|
is_array: false,
|
||||||
@ -1362,6 +1490,7 @@ impl Formatter<'_> {
|
|||||||
&items[1],
|
&items[1],
|
||||||
&self.struct_names,
|
&self.struct_names,
|
||||||
&self.enum_names,
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
) else {
|
) else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
@ -1422,7 +1551,10 @@ impl Formatter<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = expect_ident(form) {
|
if let Some(name) = expect_ident(form) {
|
||||||
if self.struct_names.contains(name) || self.enum_names.contains(name) {
|
if self.struct_names.contains(name)
|
||||||
|
|| self.enum_names.contains(name)
|
||||||
|
|| self.type_alias_names.contains(name)
|
||||||
|
{
|
||||||
return Some(name.to_string());
|
return Some(name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1439,16 +1571,20 @@ impl Formatter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(text) = render_option_result_type(items) {
|
if let Some(text) = render_option_result_type(items, &self.type_alias_names) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = render_supported_array_type(items, &self.struct_names, &self.enum_names)
|
if let Some(text) = render_supported_array_type(
|
||||||
{
|
items,
|
||||||
|
&self.struct_names,
|
||||||
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
|
) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = render_supported_vec_type(items) {
|
if let Some(text) = render_supported_vec_type(items, &self.type_alias_names) {
|
||||||
return Some(text);
|
return Some(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2051,6 +2187,7 @@ impl Formatter<'_> {
|
|||||||
&items[1],
|
&items[1],
|
||||||
&self.struct_names,
|
&self.struct_names,
|
||||||
&self.enum_names,
|
&self.enum_names,
|
||||||
|
&self.type_alias_names,
|
||||||
) else {
|
) else {
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
@ -2113,21 +2250,7 @@ impl Formatter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let payload_type = if is_ident(&items[1], "i32") {
|
let Some(payload_type) = render_payload_type_atom(&items[1], &self.type_alias_names) else {
|
||||||
"i32"
|
|
||||||
} else if is_ident(&items[1], "i64") {
|
|
||||||
"i64"
|
|
||||||
} else if is_ident(&items[1], "u32") {
|
|
||||||
"u32"
|
|
||||||
} else if is_ident(&items[1], "u64") {
|
|
||||||
"u64"
|
|
||||||
} else if is_ident(&items[1], "f64") {
|
|
||||||
"f64"
|
|
||||||
} else if is_ident(&items[1], "bool") {
|
|
||||||
"bool"
|
|
||||||
} else if is_ident(&items[1], "string") {
|
|
||||||
"string"
|
|
||||||
} else {
|
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
self.file,
|
self.file,
|
||||||
@ -2169,21 +2292,18 @@ impl Formatter<'_> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result_type = if is_ident(&items[1], "i32") && is_ident(&items[2], "i32") {
|
let Some(ok_ty) = render_payload_type_atom(&items[1], &self.type_alias_names) else {
|
||||||
"i32 i32"
|
self.errors.push(
|
||||||
} else if is_ident(&items[1], "i64") && is_ident(&items[2], "i32") {
|
Diagnostic::new(
|
||||||
"i64 i32"
|
self.file,
|
||||||
} else if is_ident(&items[1], "u32") && is_ident(&items[2], "i32") {
|
"UnsupportedFormatterForm",
|
||||||
"u32 i32"
|
"formatter supports only `(result i32 i32)`, `(result i64 i32)`, `(result u32 i32)`, `(result u64 i32)`, `(result f64 i32)`, `(result bool i32)`, and `(result string i32)` constructors",
|
||||||
} else if is_ident(&items[1], "u64") && is_ident(&items[2], "i32") {
|
)
|
||||||
"u64 i32"
|
.with_span(span),
|
||||||
} else if is_ident(&items[1], "f64") && is_ident(&items[2], "i32") {
|
);
|
||||||
"f64 i32"
|
return None;
|
||||||
} else if is_ident(&items[1], "bool") && is_ident(&items[2], "i32") {
|
};
|
||||||
"bool i32"
|
let Some(err_ty) = render_result_err_type_atom(&items[2], &self.type_alias_names) else {
|
||||||
} else if is_ident(&items[1], "string") && is_ident(&items[2], "i32") {
|
|
||||||
"string i32"
|
|
||||||
} else {
|
|
||||||
self.errors.push(
|
self.errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
self.file,
|
self.file,
|
||||||
@ -2196,7 +2316,7 @@ impl Formatter<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let value = self.render_expr(&items[3], env)?;
|
let value = self.render_expr(&items[3], env)?;
|
||||||
Some(format!("({} {} {})", name, result_type, value))
|
Some(format!("({} {} {} {})", name, ok_ty, err_ty, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_call(
|
fn render_call(
|
||||||
@ -2355,6 +2475,19 @@ fn collect_enum_names(forms: &[SExpr]) -> HashSet<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_type_alias_names(forms: &[SExpr]) -> HashSet<String> {
|
||||||
|
forms
|
||||||
|
.iter()
|
||||||
|
.filter_map(|form| {
|
||||||
|
let items = expect_list(form)?;
|
||||||
|
if !is_ident(items.first()?, "type") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
expect_ident(items.get(1)?).map(str::to_string)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn qualified_enum_name(name: &str) -> Option<(&str, &str)> {
|
fn qualified_enum_name(name: &str) -> Option<(&str, &str)> {
|
||||||
let (enum_name, variant) = name.split_once('.')?;
|
let (enum_name, variant) = name.split_once('.')?;
|
||||||
if enum_name.is_empty() || variant.is_empty() || variant.contains('.') {
|
if enum_name.is_empty() || variant.is_empty() || variant.contains('.') {
|
||||||
@ -2467,89 +2600,19 @@ fn list_head(form: &SExpr) -> Option<&str> {
|
|||||||
expect_ident(first)
|
expect_ident(first)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_option_result_type(items: &[SExpr]) -> Option<String> {
|
fn render_option_result_type(
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "i32") {
|
items: &[SExpr],
|
||||||
return Some("(option i32)".to_string());
|
type_alias_names: &HashSet<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
if items.len() == 2 && is_ident(&items[0], "option") {
|
||||||
|
let payload = render_payload_type_atom(&items[1], type_alias_names)?;
|
||||||
|
return Some(format!("(option {})", payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "i64") {
|
if items.len() == 3 && is_ident(&items[0], "result") {
|
||||||
return Some("(option i64)".to_string());
|
let ok = render_payload_type_atom(&items[1], type_alias_names)?;
|
||||||
}
|
let err = render_result_err_type_atom(&items[2], type_alias_names)?;
|
||||||
|
return Some(format!("(result {} {})", ok, err));
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "u32") {
|
|
||||||
return Some("(option u32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "u64") {
|
|
||||||
return Some("(option u64)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "f64") {
|
|
||||||
return Some("(option f64)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "bool") {
|
|
||||||
return Some("(option bool)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 2 && is_ident(&items[0], "option") && is_ident(&items[1], "string") {
|
|
||||||
return Some("(option string)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "i32")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result i32 i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "i64")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result i64 i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "u32")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result u32 i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "u64")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result u64 i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "f64")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result f64 i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "bool")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result bool i32)".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
if items.len() == 3
|
|
||||||
&& is_ident(&items[0], "result")
|
|
||||||
&& is_ident(&items[1], "string")
|
|
||||||
&& is_ident(&items[2], "i32")
|
|
||||||
{
|
|
||||||
return Some("(result string i32)".to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@ -2559,42 +2622,27 @@ fn render_supported_array_constructor_type(
|
|||||||
form: &SExpr,
|
form: &SExpr,
|
||||||
struct_names: &HashSet<String>,
|
struct_names: &HashSet<String>,
|
||||||
enum_names: &HashSet<String>,
|
enum_names: &HashSet<String>,
|
||||||
|
type_alias_names: &HashSet<String>,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
if is_ident(form, "i32") {
|
render_direct_type_atom(form, struct_names, enum_names, type_alias_names)
|
||||||
Some("i32".to_string())
|
|
||||||
} else if is_ident(form, "i64") {
|
|
||||||
Some("i64".to_string())
|
|
||||||
} else if is_ident(form, "u32") {
|
|
||||||
Some("u32".to_string())
|
|
||||||
} else if is_ident(form, "u64") {
|
|
||||||
Some("u64".to_string())
|
|
||||||
} else if is_ident(form, "f64") {
|
|
||||||
Some("f64".to_string())
|
|
||||||
} else if is_ident(form, "bool") {
|
|
||||||
Some("bool".to_string())
|
|
||||||
} else if is_ident(form, "string") {
|
|
||||||
Some("string".to_string())
|
|
||||||
} else if let Some(name) = expect_ident(form) {
|
|
||||||
if struct_names.contains(name) || enum_names.contains(name) {
|
|
||||||
Some(name.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_supported_array_type(
|
fn render_supported_array_type(
|
||||||
items: &[SExpr],
|
items: &[SExpr],
|
||||||
struct_names: &HashSet<String>,
|
struct_names: &HashSet<String>,
|
||||||
enum_names: &HashSet<String>,
|
enum_names: &HashSet<String>,
|
||||||
|
type_alias_names: &HashSet<String>,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
if items.len() != 3 || !is_ident(&items[0], "array") {
|
if items.len() != 3 || !is_ident(&items[0], "array") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let elem_ty = render_supported_array_constructor_type(&items[1], struct_names, enum_names)?;
|
let elem_ty = render_supported_array_constructor_type(
|
||||||
|
&items[1],
|
||||||
|
struct_names,
|
||||||
|
enum_names,
|
||||||
|
type_alias_names,
|
||||||
|
)?;
|
||||||
let len = expect_int(&items[2])?;
|
let len = expect_int(&items[2])?;
|
||||||
if len <= 0 {
|
if len <= 0 {
|
||||||
return None;
|
return None;
|
||||||
@ -2603,7 +2651,10 @@ fn render_supported_array_type(
|
|||||||
Some(format!("(array {} {})", elem_ty, len))
|
Some(format!("(array {} {})", elem_ty, len))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_supported_vec_type(items: &[SExpr]) -> Option<String> {
|
fn render_supported_vec_type(
|
||||||
|
items: &[SExpr],
|
||||||
|
type_alias_names: &HashSet<String>,
|
||||||
|
) -> Option<String> {
|
||||||
if items.len() == 2 && is_ident(&items[0], "vec") {
|
if items.len() == 2 && is_ident(&items[0], "vec") {
|
||||||
if is_ident(&items[1], "i32") {
|
if is_ident(&items[1], "i32") {
|
||||||
return Some("(vec i32)".to_string());
|
return Some("(vec i32)".to_string());
|
||||||
@ -2620,11 +2671,66 @@ fn render_supported_vec_type(items: &[SExpr]) -> Option<String> {
|
|||||||
if is_ident(&items[1], "string") {
|
if is_ident(&items[1], "string") {
|
||||||
return Some("(vec string)".to_string());
|
return Some("(vec string)".to_string());
|
||||||
}
|
}
|
||||||
|
if let Some(name) = expect_ident(&items[1]) {
|
||||||
|
if type_alias_names.contains(name) {
|
||||||
|
return Some(format!("(vec {})", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_payload_type_atom(form: &SExpr, type_alias_names: &HashSet<String>) -> Option<String> {
|
||||||
|
if is_ident(form, "i32") {
|
||||||
|
Some("i32".to_string())
|
||||||
|
} else if is_ident(form, "i64") {
|
||||||
|
Some("i64".to_string())
|
||||||
|
} else if is_ident(form, "u32") {
|
||||||
|
Some("u32".to_string())
|
||||||
|
} else if is_ident(form, "u64") {
|
||||||
|
Some("u64".to_string())
|
||||||
|
} else if is_ident(form, "f64") {
|
||||||
|
Some("f64".to_string())
|
||||||
|
} else if is_ident(form, "bool") {
|
||||||
|
Some("bool".to_string())
|
||||||
|
} else if is_ident(form, "string") {
|
||||||
|
Some("string".to_string())
|
||||||
|
} else if let Some(name) = expect_ident(form) {
|
||||||
|
type_alias_names.contains(name).then(|| name.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_result_err_type_atom(form: &SExpr, type_alias_names: &HashSet<String>) -> Option<String> {
|
||||||
|
if is_ident(form, "i32") {
|
||||||
|
Some("i32".to_string())
|
||||||
|
} else if let Some(name) = expect_ident(form) {
|
||||||
|
type_alias_names.contains(name).then(|| name.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_direct_type_atom(
|
||||||
|
form: &SExpr,
|
||||||
|
struct_names: &HashSet<String>,
|
||||||
|
enum_names: &HashSet<String>,
|
||||||
|
type_alias_names: &HashSet<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
if let Some(text) = render_payload_type_atom(form, type_alias_names) {
|
||||||
|
return Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = expect_ident(form)?;
|
||||||
|
if struct_names.contains(name) || enum_names.contains(name) {
|
||||||
|
Some(name.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expect_list(form: &SExpr) -> Option<&[SExpr]> {
|
fn expect_list(form: &SExpr) -> Option<&[SExpr]> {
|
||||||
match &form.kind {
|
match &form.kind {
|
||||||
SExprKind::List(items) => Some(items),
|
SExprKind::List(items) => Some(items),
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use crate::{
|
|||||||
ast::{
|
ast::{
|
||||||
BinaryOp, CImportDecl, EnumDecl, EnumVariantDecl, Expr, ExprKind, Function, MatchArm,
|
BinaryOp, CImportDecl, EnumDecl, EnumVariantDecl, Expr, ExprKind, Function, MatchArm,
|
||||||
MatchPattern, MatchPatternKind, Param, Program, StructDecl, StructField, StructInitField,
|
MatchPattern, MatchPatternKind, Param, Program, StructDecl, StructField, StructInitField,
|
||||||
Test,
|
Test, TypeAliasDecl,
|
||||||
},
|
},
|
||||||
diag::Diagnostic,
|
diag::Diagnostic,
|
||||||
sexpr::{Atom, SExpr, SExprKind},
|
sexpr::{Atom, SExpr, SExprKind},
|
||||||
@ -23,6 +23,8 @@ pub fn lower_program_with_imported_names(
|
|||||||
imported_names: &[String],
|
imported_names: &[String],
|
||||||
) -> Result<Program, Vec<Diagnostic>> {
|
) -> Result<Program, Vec<Diagnostic>> {
|
||||||
let mut module = None;
|
let mut module = None;
|
||||||
|
let mut type_aliases = Vec::new();
|
||||||
|
let mut alias_names = HashMap::new();
|
||||||
let mut enums = Vec::new();
|
let mut enums = Vec::new();
|
||||||
let mut enum_names = imported_names.iter().cloned().collect::<HashSet<_>>();
|
let mut enum_names = imported_names.iter().cloned().collect::<HashSet<_>>();
|
||||||
let mut structs = Vec::new();
|
let mut structs = Vec::new();
|
||||||
@ -49,10 +51,67 @@ pub fn lower_program_with_imported_names(
|
|||||||
}
|
}
|
||||||
Err(mut errs) => errors.append(&mut errs),
|
Err(mut errs) => errors.append(&mut errs),
|
||||||
},
|
},
|
||||||
|
Some("type") => match lower_type_alias(file, form) {
|
||||||
|
Ok(alias) => match alias_names.entry(alias.name.clone()) {
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(alias.name_span);
|
||||||
|
type_aliases.push(alias);
|
||||||
|
}
|
||||||
|
Entry::Occupied(entry) => errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"DuplicateTypeAlias",
|
||||||
|
format!("duplicate type alias `{}`", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("original type alias", *entry.get()),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Err(mut errs) => errors.append(&mut errs),
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for alias in &type_aliases {
|
||||||
|
if is_reserved_type_name(&alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!(
|
||||||
|
"type alias `{}` conflicts with a built-in type name",
|
||||||
|
alias.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.hint("choose an alias name distinct from built-in type names"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(struct_decl) = structs.iter().find(|decl| decl.name == alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with a struct", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("struct declaration", struct_decl.name_span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(enum_decl) = enums.iter().find(|decl| decl.name == alias.name) {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"TypeAliasNameConflict",
|
||||||
|
format!("type alias `{}` conflicts with an enum", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("enum declaration", enum_decl.name_span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for form in forms {
|
for form in forms {
|
||||||
match list_head(form) {
|
match list_head(form) {
|
||||||
Some("module") => match lower_module(file, form) {
|
Some("module") => match lower_module(file, form) {
|
||||||
@ -61,6 +120,7 @@ pub fn lower_program_with_imported_names(
|
|||||||
},
|
},
|
||||||
Some("enum") => {}
|
Some("enum") => {}
|
||||||
Some("struct") => {}
|
Some("struct") => {}
|
||||||
|
Some("type") => {}
|
||||||
Some("import_c") => match lower_c_import(file, form) {
|
Some("import_c") => match lower_c_import(file, form) {
|
||||||
Ok(import) => c_imports.push(import),
|
Ok(import) => c_imports.push(import),
|
||||||
Err(mut errs) => errors.append(&mut errs),
|
Err(mut errs) => errors.append(&mut errs),
|
||||||
@ -106,6 +166,7 @@ pub fn lower_program_with_imported_names(
|
|||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(Program {
|
Ok(Program {
|
||||||
module: module.unwrap_or_else(|| "main".to_string()),
|
module: module.unwrap_or_else(|| "main".to_string()),
|
||||||
|
type_aliases,
|
||||||
enums,
|
enums,
|
||||||
structs,
|
structs,
|
||||||
c_imports,
|
c_imports,
|
||||||
@ -124,6 +185,14 @@ pub fn print_program(program: &Program) -> String {
|
|||||||
output.push_str(&program.module);
|
output.push_str(&program.module);
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
|
|
||||||
|
for alias in &program.type_aliases {
|
||||||
|
output.push_str(" type ");
|
||||||
|
output.push_str(&alias.name);
|
||||||
|
output.push_str(" = ");
|
||||||
|
output.push_str(&alias.target.to_string());
|
||||||
|
output.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
for enum_decl in &program.enums {
|
for enum_decl in &program.enums {
|
||||||
output.push_str(" enum ");
|
output.push_str(" enum ");
|
||||||
output.push_str(&enum_decl.name);
|
output.push_str(&enum_decl.name);
|
||||||
@ -543,6 +612,71 @@ fn lower_module(file: &str, form: &SExpr) -> Result<String, Diagnostic> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_type_alias(file: &str, form: &SExpr) -> Result<TypeAliasDecl, Vec<Diagnostic>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let Some(items) = expect_list(form) else {
|
||||||
|
return Err(vec![Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"MalformedTypeAlias",
|
||||||
|
"type alias form must be a list",
|
||||||
|
)
|
||||||
|
.with_span(form.span)]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if items.len() != 3 {
|
||||||
|
return Err(vec![Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"MalformedTypeAlias",
|
||||||
|
"type alias form must be `(type Alias TargetType)`",
|
||||||
|
)
|
||||||
|
.with_span(form.span)
|
||||||
|
.expected("(type Alias TargetType)")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = match expect_ident(&items[1]) {
|
||||||
|
Some(name) => name.to_string(),
|
||||||
|
None => {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"InvalidTypeAliasName",
|
||||||
|
"type alias name must be an identifier",
|
||||||
|
)
|
||||||
|
.with_span(items[1].span),
|
||||||
|
);
|
||||||
|
"<error>".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let target = match lower_type(&items[2]) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"InvalidTypeAliasTarget",
|
||||||
|
"type alias target type is invalid",
|
||||||
|
)
|
||||||
|
.with_span(items[2].span)
|
||||||
|
.expected("concrete type"),
|
||||||
|
);
|
||||||
|
Type::Unit
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(TypeAliasDecl {
|
||||||
|
name,
|
||||||
|
name_span: items[1].span,
|
||||||
|
target,
|
||||||
|
target_span: items[2].span,
|
||||||
|
span: form.span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn lower_struct(file: &str, form: &SExpr) -> Result<StructDecl, Vec<Diagnostic>> {
|
fn lower_struct(file: &str, form: &SExpr) -> Result<StructDecl, Vec<Diagnostic>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let Some(items) = expect_list(form) else {
|
let Some(items) = expect_list(form) else {
|
||||||
@ -1249,6 +1383,26 @@ fn unsupported_unit_return_signature(file: &str, span: crate::token::Span) -> Di
|
|||||||
.hint("`unit` is reserved for compiler/runtime unit-producing forms")
|
.hint("`unit` is reserved for compiler/runtime unit-producing forms")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_reserved_type_name(name: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
name,
|
||||||
|
"i32"
|
||||||
|
| "i64"
|
||||||
|
| "u32"
|
||||||
|
| "u64"
|
||||||
|
| "f64"
|
||||||
|
| "bool"
|
||||||
|
| "unit"
|
||||||
|
| "string"
|
||||||
|
| "ptr"
|
||||||
|
| "slice"
|
||||||
|
| "option"
|
||||||
|
| "result"
|
||||||
|
| "array"
|
||||||
|
| "vec"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn lower_type(form: &SExpr) -> Option<Type> {
|
fn lower_type(form: &SExpr) -> Option<Type> {
|
||||||
match &form.kind {
|
match &form.kind {
|
||||||
SExprKind::Atom(Atom::Ident(name)) => match name.as_str() {
|
SExprKind::Atom(Atom::Ident(name)) => match name.as_str() {
|
||||||
|
|||||||
@ -177,6 +177,7 @@ struct ModuleUnit {
|
|||||||
local_functions: HashMap<String, DeclInfo>,
|
local_functions: HashMap<String, DeclInfo>,
|
||||||
local_structs: HashMap<String, DeclInfo>,
|
local_structs: HashMap<String, DeclInfo>,
|
||||||
local_enums: HashMap<String, DeclInfo>,
|
local_enums: HashMap<String, DeclInfo>,
|
||||||
|
local_aliases: HashMap<String, DeclInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -204,6 +205,7 @@ enum DeclKind {
|
|||||||
CImport,
|
CImport,
|
||||||
Struct,
|
Struct,
|
||||||
Enum,
|
Enum,
|
||||||
|
TypeAlias,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -2304,6 +2306,7 @@ fn parse_module(path: &Path, file: String, source: String) -> Result<ModuleUnit,
|
|||||||
errors.append(&mut errs);
|
errors.append(&mut errs);
|
||||||
Program {
|
Program {
|
||||||
module: name.clone(),
|
module: name.clone(),
|
||||||
|
type_aliases: Vec::new(),
|
||||||
enums: Vec::new(),
|
enums: Vec::new(),
|
||||||
structs: Vec::new(),
|
structs: Vec::new(),
|
||||||
c_imports: Vec::new(),
|
c_imports: Vec::new(),
|
||||||
@ -2313,7 +2316,7 @@ fn parse_module(path: &Path, file: String, source: String) -> Result<ModuleUnit,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (local_functions, local_structs, local_enums, mut duplicate_errors) =
|
let (local_functions, local_structs, local_enums, local_aliases, mut duplicate_errors) =
|
||||||
local_declarations(&file, &program);
|
local_declarations(&file, &program);
|
||||||
errors.append(&mut duplicate_errors);
|
errors.append(&mut duplicate_errors);
|
||||||
|
|
||||||
@ -2327,6 +2330,7 @@ fn parse_module(path: &Path, file: String, source: String) -> Result<ModuleUnit,
|
|||||||
local_functions,
|
local_functions,
|
||||||
local_structs,
|
local_structs,
|
||||||
local_enums,
|
local_enums,
|
||||||
|
local_aliases,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(errors)
|
Err(errors)
|
||||||
@ -2621,6 +2625,44 @@ fn external_enum_from_module(module: &ModuleUnit, name: &str) -> Option<External
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn external_enum_from_checked_module(
|
||||||
|
module: &ModuleUnit,
|
||||||
|
checked: &CheckedProgram,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExternalEnum> {
|
||||||
|
let source_enum = module
|
||||||
|
.program
|
||||||
|
.enums
|
||||||
|
.iter()
|
||||||
|
.find(|enum_decl| enum_decl.name == name)?;
|
||||||
|
let checked_enum = checked
|
||||||
|
.enums
|
||||||
|
.iter()
|
||||||
|
.find(|enum_decl| enum_decl.name == name)?;
|
||||||
|
|
||||||
|
Some(ExternalEnum {
|
||||||
|
name: name.to_string(),
|
||||||
|
span: source_enum.name_span,
|
||||||
|
variants: checked_enum
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|checked_variant| {
|
||||||
|
let source_variant = source_enum
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.find(|variant| variant.name == checked_variant.name);
|
||||||
|
ExternalEnumVariant {
|
||||||
|
name: checked_variant.name.clone(),
|
||||||
|
name_span: source_variant
|
||||||
|
.map_or(source_enum.name_span, |variant| variant.name_span),
|
||||||
|
payload_ty: checked_variant.payload_ty.clone(),
|
||||||
|
payload_ty_span: source_variant.and_then(|variant| variant.payload_ty_span),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn local_declarations(
|
fn local_declarations(
|
||||||
file: &str,
|
file: &str,
|
||||||
program: &Program,
|
program: &Program,
|
||||||
@ -2628,14 +2670,44 @@ fn local_declarations(
|
|||||||
HashMap<String, DeclInfo>,
|
HashMap<String, DeclInfo>,
|
||||||
HashMap<String, DeclInfo>,
|
HashMap<String, DeclInfo>,
|
||||||
HashMap<String, DeclInfo>,
|
HashMap<String, DeclInfo>,
|
||||||
|
HashMap<String, DeclInfo>,
|
||||||
Vec<Diagnostic>,
|
Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
let mut all = HashMap::<String, DeclInfo>::new();
|
let mut all = HashMap::<String, DeclInfo>::new();
|
||||||
let mut functions = HashMap::new();
|
let mut functions = HashMap::new();
|
||||||
let mut structs = HashMap::new();
|
let mut structs = HashMap::new();
|
||||||
let mut enums = HashMap::new();
|
let mut enums = HashMap::new();
|
||||||
|
let mut aliases = HashMap::new();
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for alias in &program.type_aliases {
|
||||||
|
let decl = DeclInfo {
|
||||||
|
span: alias.name_span,
|
||||||
|
kind: DeclKind::TypeAlias,
|
||||||
|
};
|
||||||
|
if let Some(original) = all.insert(alias.name.clone(), decl.clone()) {
|
||||||
|
if original.kind == DeclKind::CImport {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
file,
|
||||||
|
"DuplicateTopLevelName",
|
||||||
|
format!("duplicate top-level name `{}`", alias.name),
|
||||||
|
)
|
||||||
|
.with_span(alias.name_span)
|
||||||
|
.related("original declaration", original.span),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
errors.push(duplicate_name(
|
||||||
|
file,
|
||||||
|
&alias.name,
|
||||||
|
alias.name_span,
|
||||||
|
original.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aliases.insert(alias.name.clone(), decl);
|
||||||
|
}
|
||||||
|
|
||||||
for function in &program.functions {
|
for function in &program.functions {
|
||||||
let decl = DeclInfo {
|
let decl = DeclInfo {
|
||||||
span: function.span,
|
span: function.span,
|
||||||
@ -2767,7 +2839,7 @@ fn local_declarations(
|
|||||||
enums.insert(enum_decl.name.clone(), decl);
|
enums.insert(enum_decl.name.clone(), decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
(functions, structs, enums, errors)
|
(functions, structs, enums, aliases, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_workspace_packages(
|
fn validate_workspace_packages(
|
||||||
@ -3028,9 +3100,20 @@ fn resolve_and_check_workspace(
|
|||||||
let mut external_enums = Vec::new();
|
let mut external_enums = Vec::new();
|
||||||
for (name, binding) in imports {
|
for (name, binding) in imports {
|
||||||
let provider = &packages[binding.provider_package].modules[binding.provider_module];
|
let provider = &packages[binding.provider_package].modules[binding.provider_module];
|
||||||
|
let checked_provider =
|
||||||
|
checked_by_module.get(&(binding.provider_package, provider.name.clone()));
|
||||||
match binding.kind {
|
match binding.kind {
|
||||||
DeclKind::Function => {
|
DeclKind::Function => {
|
||||||
if let Some(function) =
|
if let Some(function) = checked_provider
|
||||||
|
.and_then(|program| program.functions.iter().find(|f| f.name == *name))
|
||||||
|
{
|
||||||
|
external_functions.push(ExternalFunction {
|
||||||
|
name: name.clone(),
|
||||||
|
params: function.params.iter().map(|(_, ty)| ty.clone()).collect(),
|
||||||
|
return_type: function.return_type.clone(),
|
||||||
|
foreign: false,
|
||||||
|
});
|
||||||
|
} else if let Some(function) =
|
||||||
provider.program.functions.iter().find(|f| f.name == *name)
|
provider.program.functions.iter().find(|f| f.name == *name)
|
||||||
{
|
{
|
||||||
external_functions.push(ExternalFunction {
|
external_functions.push(ExternalFunction {
|
||||||
@ -3042,7 +3125,16 @@ fn resolve_and_check_workspace(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::CImport => {
|
DeclKind::CImport => {
|
||||||
if let Some(import) =
|
if let Some(import) = checked_provider
|
||||||
|
.and_then(|program| program.c_imports.iter().find(|f| f.name == *name))
|
||||||
|
{
|
||||||
|
external_functions.push(ExternalFunction {
|
||||||
|
name: name.clone(),
|
||||||
|
params: import.params.iter().map(|(_, ty)| ty.clone()).collect(),
|
||||||
|
return_type: import.return_type.clone(),
|
||||||
|
foreign: true,
|
||||||
|
});
|
||||||
|
} else if let Some(import) =
|
||||||
provider.program.c_imports.iter().find(|f| f.name == *name)
|
provider.program.c_imports.iter().find(|f| f.name == *name)
|
||||||
{
|
{
|
||||||
external_functions.push(ExternalFunction {
|
external_functions.push(ExternalFunction {
|
||||||
@ -3054,7 +3146,15 @@ fn resolve_and_check_workspace(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::Struct => {
|
DeclKind::Struct => {
|
||||||
if let Some(struct_decl) =
|
if let Some(struct_decl) = checked_provider
|
||||||
|
.and_then(|program| program.structs.iter().find(|s| s.name == *name))
|
||||||
|
{
|
||||||
|
external_structs.push(ExternalStruct {
|
||||||
|
name: name.clone(),
|
||||||
|
fields: struct_decl.fields.clone(),
|
||||||
|
span: struct_decl.span,
|
||||||
|
});
|
||||||
|
} else if let Some(struct_decl) =
|
||||||
provider.program.structs.iter().find(|s| s.name == *name)
|
provider.program.structs.iter().find(|s| s.name == *name)
|
||||||
{
|
{
|
||||||
external_structs.push(ExternalStruct {
|
external_structs.push(ExternalStruct {
|
||||||
@ -3069,10 +3169,16 @@ fn resolve_and_check_workspace(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::Enum => {
|
DeclKind::Enum => {
|
||||||
if let Some(enum_decl) = external_enum_from_module(provider, name) {
|
if let Some(enum_decl) = checked_provider
|
||||||
|
.and_then(|program| {
|
||||||
|
external_enum_from_checked_module(provider, program, name)
|
||||||
|
})
|
||||||
|
.or_else(|| external_enum_from_module(provider, name))
|
||||||
|
{
|
||||||
external_enums.push(enum_decl);
|
external_enums.push(enum_decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DeclKind::TypeAlias => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3243,9 +3349,19 @@ fn resolve_and_check(
|
|||||||
let mut external_enums = Vec::new();
|
let mut external_enums = Vec::new();
|
||||||
for (name, binding) in imports {
|
for (name, binding) in imports {
|
||||||
let provider = &modules[by_name[&binding.provider]];
|
let provider = &modules[by_name[&binding.provider]];
|
||||||
|
let checked_provider = checked_by_module.get(&binding.provider);
|
||||||
match binding.kind {
|
match binding.kind {
|
||||||
DeclKind::Function => {
|
DeclKind::Function => {
|
||||||
if let Some(function) =
|
if let Some(function) = checked_provider
|
||||||
|
.and_then(|program| program.functions.iter().find(|f| f.name == *name))
|
||||||
|
{
|
||||||
|
external_functions.push(ExternalFunction {
|
||||||
|
name: name.clone(),
|
||||||
|
params: function.params.iter().map(|(_, ty)| ty.clone()).collect(),
|
||||||
|
return_type: function.return_type.clone(),
|
||||||
|
foreign: false,
|
||||||
|
});
|
||||||
|
} else if let Some(function) =
|
||||||
provider.program.functions.iter().find(|f| f.name == *name)
|
provider.program.functions.iter().find(|f| f.name == *name)
|
||||||
{
|
{
|
||||||
external_functions.push(ExternalFunction {
|
external_functions.push(ExternalFunction {
|
||||||
@ -3257,7 +3373,16 @@ fn resolve_and_check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::CImport => {
|
DeclKind::CImport => {
|
||||||
if let Some(import) =
|
if let Some(import) = checked_provider
|
||||||
|
.and_then(|program| program.c_imports.iter().find(|f| f.name == *name))
|
||||||
|
{
|
||||||
|
external_functions.push(ExternalFunction {
|
||||||
|
name: name.clone(),
|
||||||
|
params: import.params.iter().map(|(_, ty)| ty.clone()).collect(),
|
||||||
|
return_type: import.return_type.clone(),
|
||||||
|
foreign: true,
|
||||||
|
});
|
||||||
|
} else if let Some(import) =
|
||||||
provider.program.c_imports.iter().find(|f| f.name == *name)
|
provider.program.c_imports.iter().find(|f| f.name == *name)
|
||||||
{
|
{
|
||||||
external_functions.push(ExternalFunction {
|
external_functions.push(ExternalFunction {
|
||||||
@ -3269,7 +3394,15 @@ fn resolve_and_check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::Struct => {
|
DeclKind::Struct => {
|
||||||
if let Some(struct_decl) =
|
if let Some(struct_decl) = checked_provider
|
||||||
|
.and_then(|program| program.structs.iter().find(|s| s.name == *name))
|
||||||
|
{
|
||||||
|
external_structs.push(ExternalStruct {
|
||||||
|
name: name.clone(),
|
||||||
|
fields: struct_decl.fields.clone(),
|
||||||
|
span: struct_decl.span,
|
||||||
|
});
|
||||||
|
} else if let Some(struct_decl) =
|
||||||
provider.program.structs.iter().find(|s| s.name == *name)
|
provider.program.structs.iter().find(|s| s.name == *name)
|
||||||
{
|
{
|
||||||
external_structs.push(ExternalStruct {
|
external_structs.push(ExternalStruct {
|
||||||
@ -3284,10 +3417,16 @@ fn resolve_and_check(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclKind::Enum => {
|
DeclKind::Enum => {
|
||||||
if let Some(enum_decl) = external_enum_from_module(provider, name) {
|
if let Some(enum_decl) = checked_provider
|
||||||
|
.and_then(|program| {
|
||||||
|
external_enum_from_checked_module(provider, program, name)
|
||||||
|
})
|
||||||
|
.or_else(|| external_enum_from_module(provider, name))
|
||||||
|
{
|
||||||
external_enums.push(enum_decl);
|
external_enums.push(enum_decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DeclKind::TypeAlias => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3381,6 +3520,22 @@ fn build_export_maps(
|
|||||||
Some(kind) => {
|
Some(kind) => {
|
||||||
exports.insert(export.name.clone(), kind);
|
exports.insert(export.name.clone(), kind);
|
||||||
}
|
}
|
||||||
|
None if module.local_aliases.contains_key(&export.name) => diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&module.file,
|
||||||
|
"Visibility",
|
||||||
|
format!(
|
||||||
|
"type alias `{}` is module-local and cannot be exported",
|
||||||
|
export.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_span(export.span)
|
||||||
|
.related(
|
||||||
|
"type alias declaration",
|
||||||
|
module.local_aliases[&export.name].span,
|
||||||
|
)
|
||||||
|
.hint("export functions, structs, or enums; imported signatures see concrete target types"),
|
||||||
|
),
|
||||||
None => diagnostics.push(
|
None => diagnostics.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
&module.file,
|
&module.file,
|
||||||
@ -3428,6 +3583,7 @@ fn resolve_imports(
|
|||||||
.get(&name.name)
|
.get(&name.name)
|
||||||
.or_else(|| module.local_structs.get(&name.name))
|
.or_else(|| module.local_structs.get(&name.name))
|
||||||
.or_else(|| module.local_enums.get(&name.name))
|
.or_else(|| module.local_enums.get(&name.name))
|
||||||
|
.or_else(|| module.local_aliases.get(&name.name))
|
||||||
{
|
{
|
||||||
diagnostics.push(duplicate_name(
|
diagnostics.push(duplicate_name(
|
||||||
&module.file,
|
&module.file,
|
||||||
@ -3470,7 +3626,8 @@ fn resolve_imports(
|
|||||||
.local_functions
|
.local_functions
|
||||||
.get(&name.name)
|
.get(&name.name)
|
||||||
.or_else(|| provider.local_structs.get(&name.name))
|
.or_else(|| provider.local_structs.get(&name.name))
|
||||||
.or_else(|| provider.local_enums.get(&name.name));
|
.or_else(|| provider.local_enums.get(&name.name))
|
||||||
|
.or_else(|| provider.local_aliases.get(&name.name));
|
||||||
let exported = export_maps[provider_index].get(&name.name).copied();
|
let exported = export_maps[provider_index].get(&name.name).copied();
|
||||||
match (provider_local, exported) {
|
match (provider_local, exported) {
|
||||||
(_, Some(kind)) => {
|
(_, Some(kind)) => {
|
||||||
@ -3483,6 +3640,23 @@ fn resolve_imports(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
(Some(decl), None) if decl.kind == DeclKind::TypeAlias => diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&module.file,
|
||||||
|
"Visibility",
|
||||||
|
format!(
|
||||||
|
"type alias `{}` is module-local and cannot be imported from module `{}`",
|
||||||
|
name.name, provider.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_span(name.span)
|
||||||
|
.related_in_file(
|
||||||
|
provider.file.clone(),
|
||||||
|
"type alias declaration",
|
||||||
|
decl.span,
|
||||||
|
)
|
||||||
|
.hint("import functions, structs, or enums; function signatures expose the alias target type"),
|
||||||
|
),
|
||||||
(Some(decl), None) => diagnostics.push(
|
(Some(decl), None) => diagnostics.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
&module.file,
|
&module.file,
|
||||||
@ -3557,13 +3731,14 @@ fn resolve_workspace_imports(
|
|||||||
&packages[provider_package_index].modules[provider_module_index];
|
&packages[provider_package_index].modules[provider_module_index];
|
||||||
for name in &import.names {
|
for name in &import.names {
|
||||||
if let Some(local) = module
|
if let Some(local) = module
|
||||||
.local_functions
|
.local_functions
|
||||||
.get(&name.name)
|
.get(&name.name)
|
||||||
.or_else(|| module.local_structs.get(&name.name))
|
.or_else(|| module.local_structs.get(&name.name))
|
||||||
.or_else(|| module.local_enums.get(&name.name))
|
.or_else(|| module.local_enums.get(&name.name))
|
||||||
{
|
.or_else(|| module.local_aliases.get(&name.name))
|
||||||
diagnostics.push(duplicate_name(
|
{
|
||||||
&module.file,
|
diagnostics.push(duplicate_name(
|
||||||
|
&module.file,
|
||||||
&name.name,
|
&name.name,
|
||||||
name.span,
|
name.span,
|
||||||
local.span,
|
local.span,
|
||||||
@ -3608,7 +3783,8 @@ fn resolve_workspace_imports(
|
|||||||
.local_functions
|
.local_functions
|
||||||
.get(&name.name)
|
.get(&name.name)
|
||||||
.or_else(|| provider.local_structs.get(&name.name))
|
.or_else(|| provider.local_structs.get(&name.name))
|
||||||
.or_else(|| provider.local_enums.get(&name.name));
|
.or_else(|| provider.local_enums.get(&name.name))
|
||||||
|
.or_else(|| provider.local_aliases.get(&name.name));
|
||||||
let exported = export_maps[provider_package_index]
|
let exported = export_maps[provider_package_index]
|
||||||
[provider_module_index]
|
[provider_module_index]
|
||||||
.get(&name.name)
|
.get(&name.name)
|
||||||
@ -3625,6 +3801,25 @@ fn resolve_workspace_imports(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
(Some(decl), None) if decl.kind == DeclKind::TypeAlias => {
|
||||||
|
diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&module.file,
|
||||||
|
"Visibility",
|
||||||
|
format!(
|
||||||
|
"type alias `{}` is module-local and cannot be imported from module `{}`",
|
||||||
|
name.name, import.module
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_span(name.span)
|
||||||
|
.related_in_file(
|
||||||
|
provider.file.clone(),
|
||||||
|
"type alias declaration",
|
||||||
|
decl.span,
|
||||||
|
)
|
||||||
|
.hint("import functions, structs, or enums; function signatures expose the alias target type"),
|
||||||
|
);
|
||||||
|
}
|
||||||
(Some(decl), None) => diagnostics.push(
|
(Some(decl), None) => diagnostics.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
&module.file,
|
&module.file,
|
||||||
|
|||||||
116
compiler/tests/concrete_type_aliases_beta.rs
Normal file
116
compiler/tests/concrete_type_aliases_beta.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::{Command, Output},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concrete_type_alias_fixture_erases_before_llvm_and_runs_tests() {
|
||||||
|
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/type-aliases.slo");
|
||||||
|
|
||||||
|
let compile = run_glagol([fixture.as_os_str()]);
|
||||||
|
let stdout = String::from_utf8_lossy(&compile.stdout);
|
||||||
|
let stderr = String::from_utf8_lossy(&compile.stderr);
|
||||||
|
assert!(
|
||||||
|
compile.status.success(),
|
||||||
|
"compiler rejected concrete type alias fixture\nstdout:\n{}\nstderr:\n{}",
|
||||||
|
stdout,
|
||||||
|
stderr
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stdout.contains("define i32 @bump(i32 %value)")
|
||||||
|
&& stdout.contains("define [3 x i32] @make_counts(i32 %base)")
|
||||||
|
&& stdout.contains("define i32 @pick_count([3 x i32] %values)")
|
||||||
|
&& stdout.contains("define { i32, ptr } @make_measure(i32 %amount)")
|
||||||
|
&& stdout.contains("define { i1, i32 } @maybe_amount(i32 %amount)")
|
||||||
|
&& stdout.contains("define { i1, i32 } @ok_amount(i32 %amount)")
|
||||||
|
&& stdout.contains("define ptr @empty_counts()")
|
||||||
|
&& stdout.contains("define { i32, i32 } @reading_value(i32 %amount)")
|
||||||
|
&& stdout.contains("define i32 @reading_score({ i32, i32 } %reading)")
|
||||||
|
&& stdout.contains("call ptr @__glagol_vec_i32_empty()"),
|
||||||
|
"LLVM output did not show erased concrete alias shapes\nstdout:\n{}",
|
||||||
|
stdout
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!stdout.contains("Count")
|
||||||
|
&& !stdout.contains("Score")
|
||||||
|
&& !stdout.contains("MaybeCount")
|
||||||
|
&& !stdout.contains("ReadingAlias"),
|
||||||
|
"LLVM output leaked source alias names\nstdout:\n{}",
|
||||||
|
stdout
|
||||||
|
);
|
||||||
|
assert!(stderr.is_empty(), "compiler wrote stderr:\n{}", stderr);
|
||||||
|
|
||||||
|
let run = run_glagol([OsStr::new("--run-tests"), fixture.as_os_str()]);
|
||||||
|
assert_success_stdout(
|
||||||
|
run,
|
||||||
|
concat!(
|
||||||
|
"test \"aliases erase through arrays and structs\" ... ok\n",
|
||||||
|
"test \"aliases erase through option result vec and enum\" ... ok\n",
|
||||||
|
"2 test(s) passed\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concrete_type_alias_fixture_formats_and_lowers_stably() {
|
||||||
|
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/type-aliases.slo");
|
||||||
|
|
||||||
|
let formatted = run_glagol([OsStr::new("--format"), fixture.as_os_str()]);
|
||||||
|
assert_success_stdout(
|
||||||
|
formatted,
|
||||||
|
&std::fs::read_to_string(&fixture).expect("read type alias fixture"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let surface = run_glagol([
|
||||||
|
OsStr::new("--inspect-lowering=surface"),
|
||||||
|
fixture.as_os_str(),
|
||||||
|
]);
|
||||||
|
assert_success_stdout(
|
||||||
|
surface,
|
||||||
|
&std::fs::read_to_string(
|
||||||
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/type-aliases.surface.lower"),
|
||||||
|
)
|
||||||
|
.expect("read type alias surface snapshot"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let checked = run_glagol([
|
||||||
|
OsStr::new("--inspect-lowering=checked"),
|
||||||
|
fixture.as_os_str(),
|
||||||
|
]);
|
||||||
|
assert_success_stdout(
|
||||||
|
checked,
|
||||||
|
&std::fs::read_to_string(
|
||||||
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/type-aliases.checked.lower"),
|
||||||
|
)
|
||||||
|
.expect("read type alias checked snapshot"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_glagol<I, S>(args: I) -> Output
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
Command::new(glagol_bin())
|
||||||
|
.args(args)
|
||||||
|
.output()
|
||||||
|
.expect("run glagol")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn glagol_bin() -> PathBuf {
|
||||||
|
PathBuf::from(env!("CARGO_BIN_EXE_glagol"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_success_stdout(output: Output, expected: &str) {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
assert!(
|
||||||
|
output.status.success(),
|
||||||
|
"command failed\nstdout:\n{}\nstderr:\n{}",
|
||||||
|
stdout,
|
||||||
|
stderr
|
||||||
|
);
|
||||||
|
assert_eq!(stdout, expected);
|
||||||
|
assert!(stderr.is_empty(), "stderr was not empty:\n{}", stderr);
|
||||||
|
}
|
||||||
@ -83,6 +83,98 @@ const CASES: &[DiagnosticCase] = &[
|
|||||||
"#,
|
"#,
|
||||||
snapshot: "../tests/unknown-top-level-form.diag",
|
snapshot: "../tests/unknown-top-level-form.diag",
|
||||||
},
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "malformed-type-alias",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/malformed-type-alias.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "duplicate-type-alias",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count i32)
|
||||||
|
(type Count i64)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/duplicate-type-alias.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "type-alias-name-conflict",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(struct Count
|
||||||
|
(value i32))
|
||||||
|
|
||||||
|
(type Count i32)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/type-alias-name-conflict.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "type-alias-value-name-conflict",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(import_c c_add ((value i32)) -> i32)
|
||||||
|
|
||||||
|
(type main i32)
|
||||||
|
|
||||||
|
(type c_add i32)
|
||||||
|
|
||||||
|
(fn main () -> i32
|
||||||
|
0)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/type-alias-value-name-conflict.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "unknown-type-alias-target",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count Missing)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/unknown-type-alias-target.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "unsupported-type-alias-target",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type BadUnit unit)
|
||||||
|
|
||||||
|
(type BadPtr (ptr i32))
|
||||||
|
|
||||||
|
(type BadSlice (slice i32))
|
||||||
|
|
||||||
|
(type BadVec (vec u32))
|
||||||
|
|
||||||
|
(type BadResult (result i32 string))
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/unsupported-type-alias-target.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "self-type-alias",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count Count)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/self-type-alias.diag",
|
||||||
|
},
|
||||||
|
DiagnosticCase {
|
||||||
|
name: "cyclic-type-alias",
|
||||||
|
source: r#"
|
||||||
|
(module main)
|
||||||
|
|
||||||
|
(type A B)
|
||||||
|
(type B A)
|
||||||
|
"#,
|
||||||
|
snapshot: "../tests/cyclic-type-alias.diag",
|
||||||
|
},
|
||||||
DiagnosticCase {
|
DiagnosticCase {
|
||||||
name: "enum-empty",
|
name: "enum-empty",
|
||||||
source: r#"
|
source: r#"
|
||||||
|
|||||||
@ -13,6 +13,12 @@ const LOWERING_FIXTURES: &[LoweringFixture] = &[
|
|||||||
surface_snapshot: "../tests/top-level-test.surface.lower",
|
surface_snapshot: "../tests/top-level-test.surface.lower",
|
||||||
checked_snapshot: "../tests/top-level-test.checked.lower",
|
checked_snapshot: "../tests/top-level-test.checked.lower",
|
||||||
},
|
},
|
||||||
|
LoweringFixture {
|
||||||
|
name: "type-aliases",
|
||||||
|
source: "../tests/type-aliases.slo",
|
||||||
|
surface_snapshot: "../tests/type-aliases.surface.lower",
|
||||||
|
checked_snapshot: "../tests/type-aliases.checked.lower",
|
||||||
|
},
|
||||||
LoweringFixture {
|
LoweringFixture {
|
||||||
name: "comments",
|
name: "comments",
|
||||||
source: "../tests/comments.slo",
|
source: "../tests/comments.slo",
|
||||||
|
|||||||
@ -1279,6 +1279,63 @@ fn enum_import_visibility_and_duplicate_cases_are_diagnostics() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_aliases_are_local_across_project_visibility() {
|
||||||
|
let erased_signature = write_project(
|
||||||
|
"alias-erased-signature",
|
||||||
|
&[(
|
||||||
|
"types",
|
||||||
|
"(module types (export make_count))\n\n(type Count i32)\n\n(fn make_count ((value Count)) -> Count\n value)\n",
|
||||||
|
)],
|
||||||
|
"(module main)\n\n(import types (make_count))\n\n(fn main () -> i32\n (make_count 42))\n",
|
||||||
|
);
|
||||||
|
let erased_output = run_glagol(["check".as_ref(), erased_signature.as_os_str()]);
|
||||||
|
assert_success_stdout("alias erased project signature", erased_output, "");
|
||||||
|
|
||||||
|
let export_alias = write_project(
|
||||||
|
"alias-export",
|
||||||
|
&[(
|
||||||
|
"types",
|
||||||
|
"(module types (export Count))\n\n(type Count i32)\n\n(fn make_count ((value Count)) -> Count\n value)\n",
|
||||||
|
)],
|
||||||
|
"(module main)\n",
|
||||||
|
);
|
||||||
|
let export_output = run_glagol(["check".as_ref(), export_alias.as_os_str()]);
|
||||||
|
assert_exit_code("alias export", &export_output, 1);
|
||||||
|
assert_stderr_contains("alias export", &export_output, "Visibility");
|
||||||
|
assert_stderr_contains(
|
||||||
|
"alias export message",
|
||||||
|
&export_output,
|
||||||
|
"type alias `Count` is module-local and cannot be exported",
|
||||||
|
);
|
||||||
|
|
||||||
|
let import_alias = write_project(
|
||||||
|
"alias-import",
|
||||||
|
&[("types", "(module types)\n\n(type Count i32)\n")],
|
||||||
|
"(module main)\n\n(import types (Count))\n",
|
||||||
|
);
|
||||||
|
let import_output = run_glagol(["check".as_ref(), import_alias.as_os_str()]);
|
||||||
|
assert_exit_code("alias import", &import_output, 1);
|
||||||
|
assert_stderr_contains("alias import", &import_output, "Visibility");
|
||||||
|
assert_stderr_contains(
|
||||||
|
"alias import message",
|
||||||
|
&import_output,
|
||||||
|
"type alias `Count` is module-local and cannot be imported",
|
||||||
|
);
|
||||||
|
|
||||||
|
let duplicate_local = write_project(
|
||||||
|
"alias-import-duplicate",
|
||||||
|
&[(
|
||||||
|
"types",
|
||||||
|
"(module types (export value))\n\n(fn value () -> i32\n 1)\n",
|
||||||
|
)],
|
||||||
|
"(module main)\n\n(import types (Count))\n\n(type Count i32)\n",
|
||||||
|
);
|
||||||
|
let duplicate_output = run_glagol(["check".as_ref(), duplicate_local.as_os_str()]);
|
||||||
|
assert_exit_code("alias import duplicate", &duplicate_output, 1);
|
||||||
|
assert_stderr_contains("alias import duplicate", &duplicate_output, "DuplicateName");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn project_diagnostic_families_have_json_golden_coverage() {
|
fn project_diagnostic_families_have_json_golden_coverage() {
|
||||||
let duplicate = write_project(
|
let duplicate = write_project(
|
||||||
|
|||||||
@ -13,11 +13,13 @@ const DIAGNOSTIC_SNAPSHOTS: &[&str] = &[
|
|||||||
"array-return-type-mismatch.diag",
|
"array-return-type-mismatch.diag",
|
||||||
"arity-mismatch.diag",
|
"arity-mismatch.diag",
|
||||||
"cyclic-struct-fields.diag",
|
"cyclic-struct-fields.diag",
|
||||||
|
"cyclic-type-alias.diag",
|
||||||
"duplicate-local.diag",
|
"duplicate-local.diag",
|
||||||
"duplicate-match-arm.diag",
|
"duplicate-match-arm.diag",
|
||||||
"duplicate-struct-constructor-field.diag",
|
"duplicate-struct-constructor-field.diag",
|
||||||
"duplicate-struct-field.diag",
|
"duplicate-struct-field.diag",
|
||||||
"duplicate-struct.diag",
|
"duplicate-struct.diag",
|
||||||
|
"duplicate-type-alias.diag",
|
||||||
"empty-array.diag",
|
"empty-array.diag",
|
||||||
"enum-constructor-arity.diag",
|
"enum-constructor-arity.diag",
|
||||||
"enum-container-values.diag",
|
"enum-container-values.diag",
|
||||||
@ -65,6 +67,7 @@ const DIAGNOSTIC_SNAPSHOTS: &[&str] = &[
|
|||||||
"malformed-result-err-unwrap.diag",
|
"malformed-result-err-unwrap.diag",
|
||||||
"malformed-result-ok-unwrap.diag",
|
"malformed-result-ok-unwrap.diag",
|
||||||
"malformed-result-constructor.diag",
|
"malformed-result-constructor.diag",
|
||||||
|
"malformed-type-alias.diag",
|
||||||
"malformed-unsafe-form.diag",
|
"malformed-unsafe-form.diag",
|
||||||
"malformed-while.diag",
|
"malformed-while.diag",
|
||||||
"field-access-on-non-struct.diag",
|
"field-access-on-non-struct.diag",
|
||||||
@ -82,6 +85,7 @@ const DIAGNOSTIC_SNAPSHOTS: &[&str] = &[
|
|||||||
"set-immutable-local.diag",
|
"set-immutable-local.diag",
|
||||||
"set-parameter.diag",
|
"set-parameter.diag",
|
||||||
"single-file-main-i64-return.diag",
|
"single-file-main-i64-return.diag",
|
||||||
|
"self-type-alias.diag",
|
||||||
"set-type-mismatch.diag",
|
"set-type-mismatch.diag",
|
||||||
"set-unknown-local.diag",
|
"set-unknown-local.diag",
|
||||||
"std-abi-layout-unsupported.diag",
|
"std-abi-layout-unsupported.diag",
|
||||||
@ -280,7 +284,11 @@ const DIAGNOSTIC_SNAPSHOTS: &[&str] = &[
|
|||||||
"test-invalid-name.diag",
|
"test-invalid-name.diag",
|
||||||
"test-non-bool.diag",
|
"test-non-bool.diag",
|
||||||
"type-mismatch.diag",
|
"type-mismatch.diag",
|
||||||
|
"type-alias-name-conflict.diag",
|
||||||
|
"type-alias-value-name-conflict.diag",
|
||||||
"unclosed-list.diag",
|
"unclosed-list.diag",
|
||||||
|
"unknown-type-alias-target.diag",
|
||||||
|
"unsupported-type-alias-target.diag",
|
||||||
"unknown-function.diag",
|
"unknown-function.diag",
|
||||||
"unknown-struct-local.diag",
|
"unknown-struct-local.diag",
|
||||||
"unknown-struct-parameter.diag",
|
"unknown-struct-parameter.diag",
|
||||||
@ -465,6 +473,8 @@ const LOWERING_INSPECTOR_FIXTURES: &[&str] = &[
|
|||||||
"time-sleep.surface.lower",
|
"time-sleep.surface.lower",
|
||||||
"top-level-test.checked.lower",
|
"top-level-test.checked.lower",
|
||||||
"top-level-test.surface.lower",
|
"top-level-test.surface.lower",
|
||||||
|
"type-aliases.checked.lower",
|
||||||
|
"type-aliases.surface.lower",
|
||||||
"u32-numeric-primitive.checked.lower",
|
"u32-numeric-primitive.checked.lower",
|
||||||
"u32-numeric-primitive.surface.lower",
|
"u32-numeric-primitive.surface.lower",
|
||||||
"u64-numeric-primitive.checked.lower",
|
"u64-numeric-primitive.checked.lower",
|
||||||
@ -490,6 +500,11 @@ const LOWERING_INSPECTOR_CASES: &[LoweringInspectorCase] = &[
|
|||||||
surface_snapshot: "top-level-test.surface.lower",
|
surface_snapshot: "top-level-test.surface.lower",
|
||||||
checked_snapshot: "top-level-test.checked.lower",
|
checked_snapshot: "top-level-test.checked.lower",
|
||||||
},
|
},
|
||||||
|
LoweringInspectorCase {
|
||||||
|
source: "tests/type-aliases.slo",
|
||||||
|
surface_snapshot: "type-aliases.surface.lower",
|
||||||
|
checked_snapshot: "type-aliases.checked.lower",
|
||||||
|
},
|
||||||
LoweringInspectorCase {
|
LoweringInspectorCase {
|
||||||
source: "tests/comments.slo",
|
source: "tests/comments.slo",
|
||||||
surface_snapshot: "comments.surface.lower",
|
surface_snapshot: "comments.surface.lower",
|
||||||
@ -853,6 +868,7 @@ const GLAGOL_TEST_FIXTURES: &[&str] = &[
|
|||||||
"time-sleep.slo",
|
"time-sleep.slo",
|
||||||
"top-level-test.fmt",
|
"top-level-test.fmt",
|
||||||
"top-level-test.slo",
|
"top-level-test.slo",
|
||||||
|
"type-aliases.slo",
|
||||||
"u32-numeric-primitive.slo",
|
"u32-numeric-primitive.slo",
|
||||||
"u64-numeric-primitive.slo",
|
"u64-numeric-primitive.slo",
|
||||||
"unsafe.slo",
|
"unsafe.slo",
|
||||||
@ -920,6 +936,7 @@ const SLOVO_FORMATTER_FIXTURES: &[&str] = &[
|
|||||||
"struct.slo",
|
"struct.slo",
|
||||||
"time-sleep.slo",
|
"time-sleep.slo",
|
||||||
"top-level-test.slo",
|
"top-level-test.slo",
|
||||||
|
"type-aliases.slo",
|
||||||
"u32-numeric-primitive.slo",
|
"u32-numeric-primitive.slo",
|
||||||
"u64-numeric-primitive.slo",
|
"u64-numeric-primitive.slo",
|
||||||
"unsafe.slo",
|
"unsafe.slo",
|
||||||
|
|||||||
@ -6,6 +6,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, Output, Stdio},
|
process::{Command, Output, Stdio},
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
@ -951,11 +952,16 @@ fn write_fixture(name: &str, source: &str) -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn temp_root(name: &str) -> PathBuf {
|
fn temp_root(name: &str) -> PathBuf {
|
||||||
|
let nanos = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("system clock before Unix epoch")
|
||||||
|
.as_nanos();
|
||||||
env::temp_dir().join(format!(
|
env::temp_dir().join(format!(
|
||||||
"glagol-exp10-host-result-{}-{}-{}",
|
"glagol-exp10-host-result-{}-{}-{}-{}",
|
||||||
name,
|
name,
|
||||||
std::process::id(),
|
std::process::id(),
|
||||||
NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed)
|
NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed),
|
||||||
|
nanos
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,7 +105,8 @@ Candidate features:
|
|||||||
- `glagol run`-friendly `main` conventions and clearer entry-point diagnostics
|
- `glagol run`-friendly `main` conventions and clearer entry-point diagnostics
|
||||||
- better `match` diagnostics and exhaustiveness checks where the current enum
|
- better `match` diagnostics and exhaustiveness checks where the current enum
|
||||||
model allows it
|
model allows it
|
||||||
- type aliases for long concrete types such as vectors, options, and results
|
- concrete type aliases for long concrete types such as vectors, options, and
|
||||||
|
results
|
||||||
- destructuring for simple structs and enum payloads
|
- destructuring for simple structs and enum payloads
|
||||||
- additional numeric completeness such as `f32`, only if it can be tested across
|
- additional numeric completeness such as `f32`, only if it can be tested across
|
||||||
checker, formatter, runtime, and docs
|
checker, formatter, runtime, and docs
|
||||||
@ -117,6 +118,8 @@ Released in `1.0.0-beta.4`: project/workspace build and run entry
|
|||||||
diagnostics now use entry-specific codes and explicitly show the required
|
diagnostics now use entry-specific codes and explicitly show the required
|
||||||
`(fn main () -> i32 ...)` contract. Non-exhaustive `match` diagnostics now use
|
`(fn main () -> i32 ...)` contract. Non-exhaustive `match` diagnostics now use
|
||||||
clearer missing-arm wording and deterministic found-arm output.
|
clearer missing-arm wording and deterministic found-arm output.
|
||||||
|
Concrete aliases were split into the follow-up `1.0.0-beta.8` language slice so
|
||||||
|
the syntax, formatter, diagnostics, and source fixtures could be gated directly.
|
||||||
|
|
||||||
### 5. Package And Workspace Discipline
|
### 5. Package And Workspace Discipline
|
||||||
|
|
||||||
@ -194,7 +197,37 @@ encoding policy beyond the current runtime string ABI remain deferred.
|
|||||||
Why seventh: networking and CLI tools need data interchange, but a complete JSON
|
Why seventh: networking and CLI tools need data interchange, but a complete JSON
|
||||||
library depends on collection work.
|
library depends on collection work.
|
||||||
|
|
||||||
### 8. Generics And Collection Unification
|
### 8. Concrete Type Alias Foundation
|
||||||
|
|
||||||
|
Goal: reduce concrete type repetition without introducing generics or changing
|
||||||
|
runtime representation.
|
||||||
|
|
||||||
|
Work:
|
||||||
|
|
||||||
|
- add top-level `(type Alias TargetType)` declarations for aliases whose targets
|
||||||
|
are already supported concrete Slovo types
|
||||||
|
- resolve aliases before typed-core lowering, checked import signatures, backend
|
||||||
|
layout, ABI decisions, and runtime behavior
|
||||||
|
- keep aliases module-local: no alias exports, imports, re-exports, or
|
||||||
|
cross-module alias visibility
|
||||||
|
- update formatter and diagnostics for malformed, duplicate, unsupported,
|
||||||
|
cyclic, exported, or imported aliases
|
||||||
|
- exercise aliases sparingly in JSON source facades and explicit source
|
||||||
|
fixtures without adding compiler-known runtime names
|
||||||
|
|
||||||
|
Released in `1.0.0-beta.8`: concrete aliases such as `(type JsonText string)`
|
||||||
|
are transparent names for existing concrete types. The compiler parses,
|
||||||
|
formats, checks, lowers, and erases aliases before backend behavior, while
|
||||||
|
project imports of functions that used local aliases see concrete target
|
||||||
|
types. Alias export/import attempts and unsupported targets are diagnostics.
|
||||||
|
Generic aliases, parameterized aliases, aliasing maps/sets, stable ABI/layout
|
||||||
|
names, and runtime changes remain deferred.
|
||||||
|
|
||||||
|
Why eighth: concrete aliases remove real noise from current stdlib and fixture
|
||||||
|
code while deliberately postponing generic type parameters until the compiler
|
||||||
|
and standard library have stronger design pressure.
|
||||||
|
|
||||||
|
### 9. Generics And Collection Unification
|
||||||
|
|
||||||
Goal: stop duplicating every helper across concrete vector, option, and result
|
Goal: stop duplicating every helper across concrete vector, option, and result
|
||||||
families.
|
families.
|
||||||
@ -207,11 +240,11 @@ Work:
|
|||||||
- add generic arrays/vectors first, then maps and sets
|
- add generic arrays/vectors first, then maps and sets
|
||||||
- keep stable ABI/layout promises deferred until after real beta use
|
- keep stable ABI/layout promises deferred until after real beta use
|
||||||
|
|
||||||
Why eighth: generics are important, but they should be introduced after the
|
Why ninth: generics are important, but they should be introduced after the
|
||||||
compiler, docs, tests, and stdlib have enough real pressure to validate the
|
compiler, docs, tests, and stdlib have enough real pressure to validate the
|
||||||
design.
|
design.
|
||||||
|
|
||||||
### 9. Developer Experience
|
### 10. Developer Experience
|
||||||
|
|
||||||
Goal: make Slovo comfortable for repeated daily use.
|
Goal: make Slovo comfortable for repeated daily use.
|
||||||
|
|
||||||
@ -223,7 +256,7 @@ Work:
|
|||||||
- clearer benchmark harness output
|
- clearer benchmark harness output
|
||||||
- machine-readable diagnostics stability policy
|
- machine-readable diagnostics stability policy
|
||||||
|
|
||||||
Why ninth: editor support matters, but it should follow a stable enough syntax,
|
Why tenth: editor support matters, but it should follow a stable enough syntax,
|
||||||
project model, and diagnostic model.
|
project model, and diagnostic model.
|
||||||
|
|
||||||
## Stable `1.0.0` Gate
|
## Stable `1.0.0` Gate
|
||||||
|
|||||||
@ -10,7 +10,45 @@ integration/readiness release, not the first real beta.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
No unreleased changes yet.
|
Next scoped Glagol work is expected to use the `1.0.0-beta.9` release line.
|
||||||
|
Generics and collection unification are the likely next design pressure, but
|
||||||
|
remain unreleased until their contracts and gates are explicit.
|
||||||
|
|
||||||
|
No unreleased compiler scope is committed here yet.
|
||||||
|
|
||||||
|
## 1.0.0-beta.8
|
||||||
|
|
||||||
|
Release label: `1.0.0-beta.8`
|
||||||
|
|
||||||
|
Release date: 2026-05-22
|
||||||
|
|
||||||
|
Release state: released beta concrete type alias foundation update
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
Glagol `1.0.0-beta.8` is scoped to transparent concrete type aliases. A source
|
||||||
|
module may declare `(type Alias TargetType)` for an existing supported concrete
|
||||||
|
type and then use `Alias` in supported type positions. The alias is not a new
|
||||||
|
runtime type and must be normalized before backend/ABI behavior.
|
||||||
|
|
||||||
|
- Parse top-level `(type Alias TargetType)` declarations.
|
||||||
|
- Resolve aliases to existing supported concrete target types before typed-core
|
||||||
|
lowering, checked import signatures, LLVM/backend layout, ABI decisions, and
|
||||||
|
runtime behavior.
|
||||||
|
- Format aliases canonically as one-line top-level declarations.
|
||||||
|
- Diagnose malformed aliases, duplicate alias/type/value names, unknown or
|
||||||
|
unsupported target types, cycles, exported aliases, imported aliases, and
|
||||||
|
parameterized/generic alias attempts.
|
||||||
|
- Keep runtime behavior unchanged: no new compiler-known runtime names, hosted
|
||||||
|
symbols, maps/sets, generic aliases, parameterized aliases, or cross-module
|
||||||
|
alias visibility.
|
||||||
|
|
||||||
|
### Explicit Deferrals
|
||||||
|
|
||||||
|
This release does not add generics, parameterized aliases, alias
|
||||||
|
exports/imports, alias re-exports, cross-module alias visibility, maps/sets,
|
||||||
|
stable layout names, runtime helpers, hosted runtime symbols, or
|
||||||
|
standard-library API freeze claims.
|
||||||
|
|
||||||
## 1.0.0-beta.7
|
## 1.0.0-beta.7
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,8 @@ general-purpose beta release.
|
|||||||
|
|
||||||
A Glagol feature is done only when it has parser/lowerer support, checker behavior, diagnostics for invalid forms, backend behavior or explicit unsupported diagnostics, and tests.
|
A Glagol feature is done only when it has parser/lowerer support, checker behavior, diagnostics for invalid forms, backend behavior or explicit unsupported diagnostics, and tests.
|
||||||
|
|
||||||
Current stage: `1.0.0-beta.7`, released on 2026-05-22 as the first post-beta
|
Current stage: `1.0.0-beta.8`, released on 2026-05-22 as the first post-beta
|
||||||
serialization/data-interchange foundation update. It keeps the `1.0.0-beta` language/compiler
|
concrete type alias foundation update. It keeps the `1.0.0-beta` language/compiler
|
||||||
support baseline and includes the `1.0.0-beta.1` tooling hardening release, the
|
support baseline and includes the `1.0.0-beta.1` tooling hardening release, the
|
||||||
`1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3`
|
`1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3`
|
||||||
standard-library stabilization release, the `1.0.0-beta.4`
|
standard-library stabilization release, the `1.0.0-beta.4`
|
||||||
@ -32,7 +32,14 @@ discipline release, and the `1.0.0-beta.6` compiler-known `std.net` loopback
|
|||||||
TCP runtime family with focused lowering, interpreter, diagnostics,
|
TCP runtime family with focused lowering, interpreter, diagnostics,
|
||||||
source-facade, promotion, and hosted smoke coverage, plus the `1.0.0-beta.7`
|
source-facade, promotion, and hosted smoke coverage, plus the `1.0.0-beta.7`
|
||||||
compiler-known `std.json.quote_string` runtime family with matching
|
compiler-known `std.json.quote_string` runtime family with matching
|
||||||
source-facade, test-runner, hosted smoke, and benchmark-scaffold coverage.
|
source-facade, test-runner, hosted smoke, and benchmark-scaffold coverage, plus
|
||||||
|
top-level `(type Alias TargetType)` parsing, checking, formatting, lowering
|
||||||
|
inspection, project import normalization, and diagnostics coverage for concrete
|
||||||
|
aliases.
|
||||||
|
|
||||||
|
Next stage target: `1.0.0-beta.9`, likely generics and collection
|
||||||
|
unification. The exact compiler-side contract must be frozen from the manifest
|
||||||
|
and roadmap before implementation.
|
||||||
|
|
||||||
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
||||||
flow, parse/format runtime lanes, and matching staged stdlib helper breadth
|
flow, parse/format runtime lanes, and matching staged stdlib helper breadth
|
||||||
|
|||||||
@ -8,7 +8,7 @@ Historical `exp-*` releases listed here are experimental maturity milestones.
|
|||||||
The pushed tag `v2.0.0-beta.1` is historical. It is now documented as an
|
The pushed tag `v2.0.0-beta.1` is historical. It is now documented as an
|
||||||
experimental integration/readiness release, not as a beta maturity claim.
|
experimental integration/readiness release, not as a beta maturity claim.
|
||||||
|
|
||||||
The current release is `1.0.0-beta.7`, published on 2026-05-22. It keeps the
|
The current release is `1.0.0-beta.8`, published on 2026-05-22. It keeps the
|
||||||
`1.0.0-beta` language surface, includes the first post-beta tooling/install
|
`1.0.0-beta` language surface, includes the first post-beta tooling/install
|
||||||
hardening bundle from `1.0.0-beta.1`, and adds the first runtime/resource
|
hardening bundle from `1.0.0-beta.1`, and adds the first runtime/resource
|
||||||
foundation bundle from `1.0.0-beta.2` plus the first standard-library
|
foundation bundle from `1.0.0-beta.2` plus the first standard-library
|
||||||
@ -16,11 +16,49 @@ stabilization bundle from `1.0.0-beta.3`, the first language-usability
|
|||||||
diagnostics bundle from `1.0.0-beta.4`, and the first local
|
diagnostics bundle from `1.0.0-beta.4`, and the first local
|
||||||
package/workspace discipline bundle from `1.0.0-beta.5`, plus the first
|
package/workspace discipline bundle from `1.0.0-beta.5`, plus the first
|
||||||
loopback networking foundation bundle from `1.0.0-beta.6`, and the first
|
loopback networking foundation bundle from `1.0.0-beta.6`, and the first
|
||||||
serialization/data-interchange foundation bundle from `1.0.0-beta.7`.
|
serialization/data-interchange foundation bundle from `1.0.0-beta.7`, and the
|
||||||
|
first concrete type alias foundation from `1.0.0-beta.8`.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
No unreleased changes yet.
|
Next scoped Slovo-side work is expected to use the `1.0.0-beta.9` release
|
||||||
|
line. Generics and collection unification are the likely next design pressure,
|
||||||
|
but remain unreleased until their contracts and gates are explicit.
|
||||||
|
|
||||||
|
No unreleased language scope is committed here yet.
|
||||||
|
|
||||||
|
## 1.0.0-beta.8
|
||||||
|
|
||||||
|
Release label: `1.0.0-beta.8`
|
||||||
|
|
||||||
|
Release name: Concrete Type Alias Foundation
|
||||||
|
|
||||||
|
Release date: 2026-05-22
|
||||||
|
|
||||||
|
Status: released beta concrete type alias foundation update on the
|
||||||
|
`1.0.0-beta` language baseline.
|
||||||
|
|
||||||
|
`1.0.0-beta.8` promotes transparent type aliases for current concrete types:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type JsonText string)
|
||||||
|
```
|
||||||
|
|
||||||
|
An alias is a module-local source name. It may be used in supported type
|
||||||
|
positions, and the compiler resolves it to the existing concrete target type
|
||||||
|
before backend and ABI behavior. This release does not add generics,
|
||||||
|
parameterized aliases, cross-module alias imports, alias re-exports, maps/sets,
|
||||||
|
stable layout names, new compiler-known runtime names, or runtime changes.
|
||||||
|
|
||||||
|
- Top-level `(type Alias TargetType)` declarations name existing concrete
|
||||||
|
supported target types without introducing new runtime representations.
|
||||||
|
- Aliases are transparent before typed-core lowering, checked import
|
||||||
|
signatures, backend layout, ABI decisions, and runtime behavior.
|
||||||
|
- The JSON source facade and explicit JSON example projects use local
|
||||||
|
`JsonText` / `JsonField` aliases as narrow source fixtures.
|
||||||
|
- Alias exports, imports, re-exports, cross-module alias visibility, generic
|
||||||
|
aliases, parameterized aliases, maps/sets, and runtime changes remain out of
|
||||||
|
scope.
|
||||||
|
|
||||||
## 1.0.0-beta.7
|
## 1.0.0-beta.7
|
||||||
|
|
||||||
|
|||||||
@ -10,19 +10,25 @@ Long-horizon planning lives in
|
|||||||
release train from the historical `v2.0.0-beta.1` tag toward and beyond the
|
release train from the historical `v2.0.0-beta.1` tag toward and beyond the
|
||||||
first real general-purpose beta Slovo contract.
|
first real general-purpose beta Slovo contract.
|
||||||
|
|
||||||
Current stage: `1.0.0-beta.7`, released on 2026-05-22 as the first post-beta
|
Current stage: `1.0.0-beta.8`, released on 2026-05-22 as the first post-beta
|
||||||
serialization/data-interchange foundation update. It keeps the `1.0.0-beta` language contract and
|
concrete type alias foundation update. It keeps the `1.0.0-beta` language contract and
|
||||||
includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2`
|
includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2`
|
||||||
runtime/resource foundation release, the `1.0.0-beta.3` standard-library
|
runtime/resource foundation release, the `1.0.0-beta.3` standard-library
|
||||||
stabilization release, the `1.0.0-beta.4` language-usability diagnostics
|
stabilization release, the `1.0.0-beta.4` language-usability diagnostics
|
||||||
release, the `1.0.0-beta.5` package/workspace discipline release, and a narrow
|
release, the `1.0.0-beta.5` package/workspace discipline release, and a narrow
|
||||||
`std.net` source facade for blocking loopback TCP client/server primitives over
|
`std.net` source facade for blocking loopback TCP client/server primitives over
|
||||||
opaque `i32` handles and concrete `result` families, plus a narrow `std.json`
|
opaque `i32` handles and concrete `result` families, plus a narrow `std.json`
|
||||||
source facade for compact JSON text construction. JSON parsing, recursive JSON
|
source facade for compact JSON text construction, plus top-level module-local
|
||||||
values, maps/sets, DNS, TLS, UDP, async IO, non-loopback binding, HTTP
|
transparent aliases for supported concrete target types. JSON parsing,
|
||||||
frameworks, rich host-error ADTs, stable ABI/layout, and a stable
|
recursive JSON values, generic aliases, parameterized aliases, cross-module
|
||||||
|
alias visibility, maps/sets, DNS, TLS, UDP, async IO, non-loopback binding,
|
||||||
|
HTTP frameworks, rich host-error ADTs, stable ABI/layout, and a stable
|
||||||
standard-library API freeze remain deferred.
|
standard-library API freeze remain deferred.
|
||||||
|
|
||||||
|
Next stage target: `1.0.0-beta.9`, likely generics and collection
|
||||||
|
unification. The exact scope must be frozen from the manifest and roadmap
|
||||||
|
before implementation.
|
||||||
|
|
||||||
The final experimental precursor scope is `exp-125`, defined in
|
The final experimental precursor scope is `exp-125`, defined in
|
||||||
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
||||||
unsigned direct-value flow, parse/format runtime lanes, and matching staged
|
unsigned direct-value flow, parse/format runtime lanes, and matching staged
|
||||||
|
|||||||
@ -107,6 +107,13 @@ Current v1 release surface and explicit experimental targets:
|
|||||||
v1.1-v1.7 surface is supported for small real flat local projects through
|
v1.1-v1.7 surface is supported for small real flat local projects through
|
||||||
`glagol new`, `check`, `fmt --check/--write`, `test`, `build`, `doc`,
|
`glagol new`, `check`, `fmt --check/--write`, `test`, `build`, `doc`,
|
||||||
artifact manifests, JSON diagnostics, and the release-gate script
|
artifact manifests, JSON diagnostics, and the release-gate script
|
||||||
|
- `1.0.0-beta.8` concrete type alias target: top-level
|
||||||
|
`(type Alias TargetType)` declarations name existing supported concrete
|
||||||
|
target types and are resolved before typed-core lowering, backend layout, ABI
|
||||||
|
decisions, and runtime behavior; no generic aliases, parameterized
|
||||||
|
aliases, alias exports/imports/re-exports, cross-module alias visibility,
|
||||||
|
maps/sets, or runtime changes are included, once the matching Glagol gates
|
||||||
|
pass
|
||||||
- `exp-1` owned runtime strings: compiler-known `std.string.concat` accepts two
|
- `exp-1` owned runtime strings: compiler-known `std.string.concat` accepts two
|
||||||
`string` values and returns an immutable runtime-owned `string`; existing
|
`string` values and returns an immutable runtime-owned `string`; existing
|
||||||
string equality, length, printing, locals, parameters, returns, and calls work
|
string equality, length, printing, locals, parameters, returns, and calls work
|
||||||
@ -1108,6 +1115,71 @@ schema validation, Unicode normalization, stable text encoding policy beyond
|
|||||||
the current null-terminated runtime string ABI, stable runtime helper symbols,
|
the current null-terminated runtime string ABI, stable runtime helper symbols,
|
||||||
and stable standard-library API guarantees remain deferred.
|
and stable standard-library API guarantees remain deferred.
|
||||||
|
|
||||||
|
### 4.4.6 Post-Beta Concrete Type Alias Foundation
|
||||||
|
|
||||||
|
Status: released in `1.0.0-beta.8` with matching Glagol parser, checker,
|
||||||
|
formatter, diagnostics, fixture, documentation, and promotion gates.
|
||||||
|
|
||||||
|
`1.0.0-beta.8` promotes transparent aliases for existing concrete type forms:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type JsonText string)
|
||||||
|
(type Scores (vec i32))
|
||||||
|
(type MaybeName (option string))
|
||||||
|
(type ReadResult (result string i32))
|
||||||
|
```
|
||||||
|
|
||||||
|
The declaration form is exactly `(type Alias TargetType)` at top level. `Alias`
|
||||||
|
must be a single user type name. `TargetType` must resolve to an already
|
||||||
|
supported concrete Slovo type: direct scalar and string types, current fixed
|
||||||
|
array families, current concrete vec families, current concrete option/result
|
||||||
|
families, current known struct names, or current enum names where that type is
|
||||||
|
already legal in the use position.
|
||||||
|
|
||||||
|
Aliases are transparent. Using `Alias` in a type position is equivalent to
|
||||||
|
using its resolved target type. The resolved target controls value flow,
|
||||||
|
operators, constructors, `match`, field access, imports of functions that use
|
||||||
|
the type, lowering, runtime behavior, and diagnostics. The alias does not create
|
||||||
|
a nominal type, wrapper, cast, runtime tag, layout name, hosted symbol, or C ABI
|
||||||
|
boundary.
|
||||||
|
|
||||||
|
Alias declarations are module-local. They may be used by later declarations in
|
||||||
|
the same module and by exported functions or structs after normalization, but
|
||||||
|
the alias name itself is not exportable, importable, or re-exportable. A module
|
||||||
|
that imports a function whose signature used a local alias sees the normalized
|
||||||
|
concrete target type, not the alias name.
|
||||||
|
|
||||||
|
Name resolution for type positions checks local concrete aliases alongside
|
||||||
|
local struct and enum type names before applying builtin/concrete type forms.
|
||||||
|
Duplicate alias, struct, enum, function, import-list, export-list, local, or
|
||||||
|
parameter names remain diagnostics under the existing duplicate-name policy.
|
||||||
|
Aliases must not shadow builtins, compiler-known standard-runtime names, or
|
||||||
|
reserved unsafe heads.
|
||||||
|
|
||||||
|
Formatter output keeps aliases as one-line top-level declarations:
|
||||||
|
|
||||||
|
```slo
|
||||||
|
(type JsonText string)
|
||||||
|
```
|
||||||
|
|
||||||
|
An alias target that is already a parenthesized concrete type form stays inline,
|
||||||
|
for example `(type JsonItems (vec string))`. Comments are allowed around the
|
||||||
|
top-level alias declaration under the existing full-line comment rules, not
|
||||||
|
inside the declaration form.
|
||||||
|
|
||||||
|
Required diagnostics include malformed alias declarations, duplicate alias
|
||||||
|
names, unknown target types, unsupported target types, cyclic aliases,
|
||||||
|
parameterized alias attempts, exported aliases, imported aliases, and
|
||||||
|
cross-module alias references. An implementation may use precise diagnostic
|
||||||
|
codes, but it must not silently treat an alias as `string`, `i32`, or any other
|
||||||
|
fallback type after a failed resolution.
|
||||||
|
|
||||||
|
This target explicitly does not add generic aliases, parameterized aliases,
|
||||||
|
alias type parameters, higher-kinded aliases, alias re-exports, cross-module
|
||||||
|
alias imports, import aliases, glob imports, maps/sets, alias-driven overloads,
|
||||||
|
implicit casts, new runtime helpers, standard-runtime names, stable ABI/layout
|
||||||
|
promises, or a stable standard-library API freeze.
|
||||||
|
|
||||||
## 4.5 v2.0.0-beta.1 Experimental Integration Readiness
|
## 4.5 v2.0.0-beta.1 Experimental Integration Readiness
|
||||||
|
|
||||||
Status: current experimental Slovo-side release contract, released 2026-05-17.
|
Status: current experimental Slovo-side release contract, released 2026-05-17.
|
||||||
@ -5442,6 +5514,7 @@ comments inside:
|
|||||||
|
|
||||||
- `(module ...)` forms
|
- `(module ...)` forms
|
||||||
- `(struct ...)` forms
|
- `(struct ...)` forms
|
||||||
|
- `(type ...)` alias declaration forms
|
||||||
- `fn` and `test` headers before their body begins
|
- `fn` and `test` headers before their body begins
|
||||||
- type forms such as `(array i32 N)`, `(option i32)`, `(result i32 i32)`,
|
- type forms such as `(array i32 N)`, `(option i32)`, `(result i32 i32)`,
|
||||||
exp-2 `(vec i32)`, exp-94 `(vec i64)`, exp-103 `(vec f64)`, and exp-4
|
exp-2 `(vec i32)`, exp-94 `(vec i64)`, exp-103 `(vec f64)`, and exp-4
|
||||||
|
|||||||
@ -6,7 +6,7 @@ Do not edit this file by hand.
|
|||||||
## Stability Tiers
|
## Stability Tiers
|
||||||
|
|
||||||
- `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line.
|
- `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line.
|
||||||
- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.7`; future releases may mark new helpers this way before they graduate.
|
- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.8`; future releases may mark new helpers this way before they graduate.
|
||||||
- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog.
|
- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog.
|
||||||
|
|
||||||
The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze.
|
The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze.
|
||||||
|
|||||||
@ -12,6 +12,8 @@ runtime/resource foundation APIs. `1.0.0-beta.3` adds the generated stdlib API
|
|||||||
catalog and the checked `projects/stdlib-composition/` example.
|
catalog and the checked `projects/stdlib-composition/` example.
|
||||||
`1.0.0-beta.7` adds explicit `projects/std-import-json/` and
|
`1.0.0-beta.7` adds explicit `projects/std-import-json/` and
|
||||||
`projects/std-layout-local-json/` examples for compact JSON text construction.
|
`projects/std-layout-local-json/` examples for compact JSON text construction.
|
||||||
|
`1.0.0-beta.8` reuses those JSON projects for local `JsonText`/`JsonField`
|
||||||
|
concrete type alias fixtures.
|
||||||
The language
|
The language
|
||||||
baseline absorbs the final exp-125 unsigned precursor scope alongside the
|
baseline absorbs the final exp-125 unsigned precursor scope alongside the
|
||||||
already promoted project/package, stdlib-source, collection, composite-data,
|
already promoted project/package, stdlib-source, collection, composite-data,
|
||||||
|
|||||||
79
docs/language/examples/formatter/type-aliases.slo
Normal file
79
docs/language/examples/formatter/type-aliases.slo
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count i32)
|
||||||
|
|
||||||
|
(type Score Count)
|
||||||
|
|
||||||
|
(type Text string)
|
||||||
|
|
||||||
|
(type Counts (array Count 3))
|
||||||
|
|
||||||
|
(type CountVec (vec Count))
|
||||||
|
|
||||||
|
(type MaybeCount (option Count))
|
||||||
|
|
||||||
|
(type CountResult (result Count Count))
|
||||||
|
|
||||||
|
(struct Measurement
|
||||||
|
(amount Count)
|
||||||
|
(label Text))
|
||||||
|
|
||||||
|
(type Measure Measurement)
|
||||||
|
|
||||||
|
(enum Reading
|
||||||
|
Empty
|
||||||
|
(Value Count))
|
||||||
|
|
||||||
|
(type ReadingAlias Reading)
|
||||||
|
|
||||||
|
(fn bump ((value Count)) -> Score
|
||||||
|
(+ value 1))
|
||||||
|
|
||||||
|
(fn make_counts ((base Count)) -> Counts
|
||||||
|
(array Count base (+ base 1) (+ base 2)))
|
||||||
|
|
||||||
|
(fn pick_count ((values Counts)) -> Count
|
||||||
|
(index values 1))
|
||||||
|
|
||||||
|
(fn make_measure ((amount Count)) -> Measure
|
||||||
|
(Measurement (amount amount) (label "ok")))
|
||||||
|
|
||||||
|
(fn measure_amount ((measure Measure)) -> Count
|
||||||
|
(. measure amount))
|
||||||
|
|
||||||
|
(fn maybe_amount ((amount Count)) -> MaybeCount
|
||||||
|
(some Count amount))
|
||||||
|
|
||||||
|
(fn maybe_empty () -> MaybeCount
|
||||||
|
(none Count))
|
||||||
|
|
||||||
|
(fn ok_amount ((amount Count)) -> CountResult
|
||||||
|
(ok Count Count amount))
|
||||||
|
|
||||||
|
(fn err_amount ((code Count)) -> CountResult
|
||||||
|
(err Count Count code))
|
||||||
|
|
||||||
|
(fn empty_counts () -> CountVec
|
||||||
|
(std.vec.i32.empty))
|
||||||
|
|
||||||
|
(fn reading_value ((amount Count)) -> ReadingAlias
|
||||||
|
(Reading.Value amount))
|
||||||
|
|
||||||
|
(fn reading_score ((reading ReadingAlias)) -> Count
|
||||||
|
(match reading
|
||||||
|
((Reading.Empty)
|
||||||
|
0)
|
||||||
|
((Reading.Value amount)
|
||||||
|
amount)))
|
||||||
|
|
||||||
|
(fn main () -> i32
|
||||||
|
(let values Counts (make_counts 40))
|
||||||
|
(+ (pick_count values) (measure_amount (make_measure 1))))
|
||||||
|
|
||||||
|
(test "aliases erase through arrays and structs"
|
||||||
|
(= (main) 42))
|
||||||
|
|
||||||
|
(test "aliases erase through option result vec and enum"
|
||||||
|
(let maybe MaybeCount (maybe_amount 7))
|
||||||
|
(let result CountResult (ok_amount 8))
|
||||||
|
(and (is_some maybe) (and (is_ok result) (and (= (std.vec.i32.len (empty_counts)) 0) (= (reading_score (reading_value 9)) 9)))))
|
||||||
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
Sanjin Gumbarevic<br>
|
Sanjin Gumbarevic<br>
|
||||||
hermeticum_lab@protonmail.com
|
hermeticum_lab@protonmail.com
|
||||||
|
|
||||||
Publication release: `1.0.0-beta.7`
|
Publication release: `1.0.0-beta.8`
|
||||||
|
|
||||||
Technical behavior baseline: compiler and language support through
|
Technical behavior baseline: compiler and language support through
|
||||||
`1.0.0-beta`; tooling and install workflow through `1.0.0-beta.1`;
|
`1.0.0-beta`; tooling and install workflow through `1.0.0-beta.1`;
|
||||||
@ -13,12 +13,13 @@ runtime/resource foundation through `1.0.0-beta.2`; standard-library
|
|||||||
stabilization through `1.0.0-beta.3`; language-usability diagnostics through
|
stabilization through `1.0.0-beta.3`; language-usability diagnostics through
|
||||||
`1.0.0-beta.4`; package/workspace discipline through `1.0.0-beta.5`;
|
`1.0.0-beta.4`; package/workspace discipline through `1.0.0-beta.5`;
|
||||||
loopback networking foundation through `1.0.0-beta.6`;
|
loopback networking foundation through `1.0.0-beta.6`;
|
||||||
serialization/data-interchange foundation through `1.0.0-beta.7`
|
serialization/data-interchange foundation through `1.0.0-beta.7`;
|
||||||
|
concrete type alias foundation through `1.0.0-beta.8`
|
||||||
|
|
||||||
Date: 2026-05-22
|
Date: 2026-05-22
|
||||||
|
|
||||||
Evidence source: paired local Slovo/Glagol monorepo verification and benchmark
|
Evidence source: paired local Slovo/Glagol monorepo verification and benchmark
|
||||||
reruns from a local checkout; beta.7 release-gate verification from the public
|
reruns from a local checkout; beta.8 release-gate verification from the public
|
||||||
monorepo
|
monorepo
|
||||||
|
|
||||||
Maturity: beta
|
Maturity: beta
|
||||||
@ -30,23 +31,26 @@ Slovo. It exists to make the language support boundary inspectable: tokens,
|
|||||||
S-expression tree, AST, typed AST, LLVM IR, hosted native executable, tests,
|
S-expression tree, AST, typed AST, LLVM IR, hosted native executable, tests,
|
||||||
diagnostics, and release documents should agree.
|
diagnostics, and release documents should agree.
|
||||||
|
|
||||||
The current publication release, `1.0.0-beta.7`, keeps the first real
|
The current publication release, `1.0.0-beta.8`, keeps the first real
|
||||||
general-purpose beta toolchain baseline from `1.0.0-beta` and records the
|
general-purpose beta toolchain baseline from `1.0.0-beta` and records the
|
||||||
first post-beta tooling/install hardening update plus the first
|
first post-beta tooling/install hardening update plus the first
|
||||||
runtime/resource foundation update plus the first standard-library
|
runtime/resource foundation update plus the first standard-library
|
||||||
stabilization update plus the first language-usability diagnostics update and
|
stabilization update plus the first language-usability diagnostics update and
|
||||||
the first local package/workspace discipline update plus the first loopback
|
the first local package/workspace discipline update plus the first loopback
|
||||||
networking foundation update plus the first serialization/data-interchange
|
networking foundation update plus the first serialization/data-interchange
|
||||||
foundation update. The beta baseline includes the completed `u32` / `u64`
|
foundation update plus the first concrete type alias foundation. The beta
|
||||||
unsigned compiler and stdlib breadth scope, the narrow `std.net` loopback TCP
|
baseline includes the completed `u32` / `u64` unsigned compiler and stdlib
|
||||||
runtime family, the narrow `std.json.quote_string` runtime family, and the
|
breadth scope, the narrow `std.net` loopback TCP runtime family, the narrow
|
||||||
current ten-scaffold benchmark suite. This paper records the current beta
|
`std.json.quote_string` runtime family, transparent concrete alias parsing and
|
||||||
|
erasure, and the current ten-scaffold benchmark suite. This paper records the
|
||||||
|
current beta
|
||||||
implementation surface, the benchmark method and results, the distinction
|
implementation surface, the benchmark method and results, the distinction
|
||||||
between Glagol and Lisp-family implementations, the beta.1 tooling update, the
|
between Glagol and Lisp-family implementations, the beta.1 tooling update, the
|
||||||
beta.2 runtime/resource foundation, the beta.3 standard-library stabilization
|
beta.2 runtime/resource foundation, the beta.3 standard-library stabilization
|
||||||
slice, the beta.4 diagnostics usability slice, the beta.5 package discipline
|
slice, the beta.4 diagnostics usability slice, the beta.5 package discipline
|
||||||
slice, the beta.6 networking foundation slice, the beta.7 serialization
|
slice, the beta.6 networking foundation slice, the beta.7 serialization
|
||||||
foundation slice, and the compiler path from beta to stable.
|
foundation slice, the beta.8 concrete alias foundation slice, and the compiler
|
||||||
|
path from beta to stable.
|
||||||
|
|
||||||
## 1. Compiler Thesis
|
## 1. Compiler Thesis
|
||||||
|
|
||||||
@ -128,12 +132,14 @@ At the current technical behavior beta baseline, Glagol supports:
|
|||||||
staged source-authored `std/*.slo` gates
|
staged source-authored `std/*.slo` gates
|
||||||
- compact JSON string literal construction through `std.json.quote_string` and
|
- compact JSON string literal construction through `std.json.quote_string` and
|
||||||
the hosted `__glagol_json_quote_string` runtime helper
|
the hosted `__glagol_json_quote_string` runtime helper
|
||||||
|
- transparent concrete type aliases erased before typed lowering, import
|
||||||
|
signature use, backend layout, ABI, and runtime behavior
|
||||||
- scalar C FFI imports
|
- scalar C FFI imports
|
||||||
- benchmark scaffolds for Slovo, C, Rust, Python, Clojure, and Common
|
- benchmark scaffolds for Slovo, C, Rust, Python, Clojure, and Common
|
||||||
Lisp/SBCL, with `cold-process` and `hot-loop` timing modes
|
Lisp/SBCL, with `cold-process` and `hot-loop` timing modes
|
||||||
|
|
||||||
The current release, `1.0.0-beta.7`, is a beta serialization/data-interchange
|
The current release, `1.0.0-beta.8`, is a beta concrete type alias foundation
|
||||||
foundation update on the first release line that may honestly use beta maturity
|
update on the first release line that may honestly use beta maturity
|
||||||
language for this toolchain.
|
language for this toolchain.
|
||||||
|
|
||||||
## 4. Diagnostics And Support Discipline
|
## 4. Diagnostics And Support Discipline
|
||||||
@ -303,10 +309,11 @@ baseline. `1.0.0-beta.1` changes tooling and install workflow, and
|
|||||||
standard-library catalog and composition coverage, `1.0.0-beta.4` improves
|
standard-library catalog and composition coverage, `1.0.0-beta.4` improves
|
||||||
diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and
|
diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and
|
||||||
`1.0.0-beta.6` adds a narrow loopback networking foundation, and
|
`1.0.0-beta.6` adds a narrow loopback networking foundation, and
|
||||||
`1.0.0-beta.7` adds a narrow JSON construction foundation. None of these
|
`1.0.0-beta.7` adds a narrow JSON construction foundation, and
|
||||||
post-beta slices claims changed benchmark performance. The beta.7
|
`1.0.0-beta.8` adds transparent concrete type aliases. None of these post-beta
|
||||||
`json-quote-loop` scaffold is present for local follow-up timing and is not
|
slices claims changed benchmark performance. The beta.7 `json-quote-loop`
|
||||||
part of the exp-123 nine-row result table below.
|
scaffold is present for local follow-up timing and is not part of the exp-123
|
||||||
|
nine-row result table below.
|
||||||
|
|
||||||
The exp-123 publication baseline widened the paired same-machine result set
|
The exp-123 publication baseline widened the paired same-machine result set
|
||||||
from seven rows to nine by adding two owned-vector kernels:
|
from seven rows to nine by adding two owned-vector kernels:
|
||||||
@ -394,15 +401,15 @@ coverage and compatibility:
|
|||||||
- package behavior becoming stable before dependency, manifest, and versioning
|
- package behavior becoming stable before dependency, manifest, and versioning
|
||||||
rules are precise
|
rules are precise
|
||||||
|
|
||||||
## 9. Path Beyond `1.0.0-beta.7`
|
## 9. Path Beyond `1.0.0-beta.8`
|
||||||
|
|
||||||
Glagol now implements the first real beta Slovo contract, the first
|
Glagol now implements the first real beta Slovo contract, the first
|
||||||
post-beta tooling/install hardening release, the first runtime/resource
|
post-beta tooling/install hardening release, the first runtime/resource
|
||||||
foundation release, the first standard-library stabilization release, and the
|
foundation release, the first standard-library stabilization release, and the
|
||||||
first diagnostics usability release, the first package/workspace discipline
|
first diagnostics usability release, the first package/workspace discipline
|
||||||
release, the first loopback networking foundation release, and the first
|
release, the first loopback networking foundation release, and the first
|
||||||
serialization/data-interchange foundation release. The remaining path is from
|
serialization/data-interchange foundation release, and the first concrete type
|
||||||
beta to stable.
|
alias foundation release. The remaining path is from beta to stable.
|
||||||
|
|
||||||
Recommended compiler sequence:
|
Recommended compiler sequence:
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
Sanjin Gumbarevic<br>
|
Sanjin Gumbarevic<br>
|
||||||
hermeticum_lab@protonmail.com
|
hermeticum_lab@protonmail.com
|
||||||
|
|
||||||
Publication release: `1.0.0-beta.7`
|
Publication release: `1.0.0-beta.8`
|
||||||
|
|
||||||
Technical behavior baseline: language surface through `1.0.0-beta`; tooling
|
Technical behavior baseline: language surface through `1.0.0-beta`; tooling
|
||||||
and install workflow through `1.0.0-beta.1`; runtime/resource foundation through
|
and install workflow through `1.0.0-beta.1`; runtime/resource foundation through
|
||||||
@ -13,12 +13,12 @@ and install workflow through `1.0.0-beta.1`; runtime/resource foundation through
|
|||||||
language-usability diagnostics through `1.0.0-beta.4`; package/workspace
|
language-usability diagnostics through `1.0.0-beta.4`; package/workspace
|
||||||
discipline through `1.0.0-beta.5`; loopback networking foundation through
|
discipline through `1.0.0-beta.5`; loopback networking foundation through
|
||||||
`1.0.0-beta.6`; serialization/data-interchange foundation through
|
`1.0.0-beta.6`; serialization/data-interchange foundation through
|
||||||
`1.0.0-beta.7`
|
`1.0.0-beta.7`; concrete type alias foundation through `1.0.0-beta.8`
|
||||||
|
|
||||||
Date: 2026-05-22
|
Date: 2026-05-22
|
||||||
|
|
||||||
Evidence source: paired local Slovo/Glagol monorepo verification and benchmark
|
Evidence source: paired local Slovo/Glagol monorepo verification and benchmark
|
||||||
reruns from a local checkout; beta.7 release-gate verification from the public
|
reruns from a local checkout; beta.8 release-gate verification from the public
|
||||||
monorepo
|
monorepo
|
||||||
|
|
||||||
Maturity: beta
|
Maturity: beta
|
||||||
@ -33,31 +33,34 @@ explicit types, explicit failure through `option` and `result`, lexical
|
|||||||
`unsafe`, and native compilation through the Glagol compiler to LLVM IR and
|
`unsafe`, and native compilation through the Glagol compiler to LLVM IR and
|
||||||
hosted executables.
|
hosted executables.
|
||||||
|
|
||||||
The current publication release, `1.0.0-beta.7`, keeps the first real
|
The current publication release, `1.0.0-beta.8`, keeps the first real
|
||||||
general-purpose beta language baseline from `1.0.0-beta` and records the first
|
general-purpose beta language baseline from `1.0.0-beta` and records the first
|
||||||
post-beta tooling/install hardening update plus the first runtime/resource
|
post-beta tooling/install hardening update plus the first runtime/resource
|
||||||
foundation update, the first standard-library stabilization update, and the
|
foundation update, the first standard-library stabilization update, and the
|
||||||
first language-usability diagnostics update, plus the first local
|
first language-usability diagnostics update, plus the first local
|
||||||
package/workspace discipline update, the first loopback networking foundation
|
package/workspace discipline update, the first loopback networking foundation
|
||||||
update, and the first serialization/data-interchange foundation update. The
|
update, the first serialization/data-interchange foundation update, and the
|
||||||
beta baseline includes the completed `u32` / `u64`
|
first concrete type alias foundation. The beta baseline includes the completed
|
||||||
|
`u32` / `u64`
|
||||||
unsigned scope, the staged stdlib breadth that makes ordinary command-line
|
unsigned scope, the staged stdlib breadth that makes ordinary command-line
|
||||||
programs practical, the current narrow `std.net` loopback TCP surface, the
|
programs practical, the current narrow `std.net` loopback TCP surface, the
|
||||||
current narrow `std.json` text-construction surface, and the current
|
current narrow `std.json` text-construction surface, module-local transparent
|
||||||
ten-scaffold benchmark suite. This paper records the current beta
|
aliases for supported concrete types, and the current ten-scaffold benchmark
|
||||||
|
suite. This paper records the current beta
|
||||||
technical state, the difference between Slovo and Lisp-family languages, the
|
technical state, the difference between Slovo and Lisp-family languages, the
|
||||||
benchmark methodology, the beta.1 tooling update, the beta.2 runtime/resource
|
benchmark methodology, the beta.1 tooling update, the beta.2 runtime/resource
|
||||||
foundation, the beta.3 standard-library stabilization slice, the beta.4
|
foundation, the beta.3 standard-library stabilization slice, the beta.4
|
||||||
diagnostics usability slice, the beta.5 package discipline slice, the beta.6
|
diagnostics usability slice, the beta.5 package discipline slice, the beta.6
|
||||||
networking foundation slice, the beta.7 serialization foundation slice, and
|
networking foundation slice, the beta.7 serialization foundation slice, and
|
||||||
the remaining path from beta to stable.
|
the beta.8 concrete alias foundation slice, and the remaining path from beta
|
||||||
|
to stable.
|
||||||
|
|
||||||
## 1. Scope
|
## 1. Scope
|
||||||
|
|
||||||
This document is a technical state paper for the current beta baseline. It
|
This document is a technical state paper for the current beta baseline. It
|
||||||
summarizes the behavior represented by the paired local Slovo and Glagol
|
summarizes the behavior represented by the paired local Slovo and Glagol
|
||||||
workspaces, with `1.0.0-beta` as the current language-surface baseline and
|
workspaces, with `1.0.0-beta` as the current language-surface baseline and
|
||||||
`1.0.0-beta.7` as the current publication baseline.
|
`1.0.0-beta.8` as the current publication baseline.
|
||||||
|
|
||||||
The support rule remains strict:
|
The support rule remains strict:
|
||||||
|
|
||||||
@ -69,7 +72,7 @@ The support rule remains strict:
|
|||||||
- partial parser recognition or speculative examples do not count as support
|
- partial parser recognition or speculative examples do not count as support
|
||||||
|
|
||||||
Historical `exp-*` releases remain experimental alpha maturity. The current
|
Historical `exp-*` releases remain experimental alpha maturity. The current
|
||||||
publication accompanies `1.0.0-beta.7`.
|
publication accompanies `1.0.0-beta.8`.
|
||||||
|
|
||||||
## 2. Design Thesis
|
## 2. Design Thesis
|
||||||
|
|
||||||
@ -380,10 +383,11 @@ baseline. `1.0.0-beta.1` changes tooling and install workflow, and
|
|||||||
standard-library catalog and composition coverage, `1.0.0-beta.4` improves
|
standard-library catalog and composition coverage, `1.0.0-beta.4` improves
|
||||||
diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and
|
diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and
|
||||||
`1.0.0-beta.6` adds a narrow loopback networking foundation, and
|
`1.0.0-beta.6` adds a narrow loopback networking foundation, and
|
||||||
`1.0.0-beta.7` adds a narrow JSON construction foundation. None of these
|
`1.0.0-beta.7` adds a narrow JSON construction foundation, and
|
||||||
post-beta slices claims changed benchmark performance. The beta.7
|
`1.0.0-beta.8` adds transparent concrete type aliases. None of these post-beta
|
||||||
`json-quote-loop` scaffold is present for local follow-up timing and is not
|
slices claims changed benchmark performance. The beta.7 `json-quote-loop`
|
||||||
part of the exp-123 nine-row result table below.
|
scaffold is present for local follow-up timing and is not part of the exp-123
|
||||||
|
nine-row result table below.
|
||||||
|
|
||||||
The exp-123 publication baseline widened the paired same-machine result set
|
The exp-123 publication baseline widened the paired same-machine result set
|
||||||
from seven rows to nine by adding two owned-vector kernels:
|
from seven rows to nine by adding two owned-vector kernels:
|
||||||
@ -504,7 +508,7 @@ Major remaining gaps before `1.0.0`:
|
|||||||
- semantic versioning and deprecation policy
|
- semantic versioning and deprecation policy
|
||||||
- a clear separation between stable and experimental features
|
- a clear separation between stable and experimental features
|
||||||
|
|
||||||
## 10. Path Beyond `1.0.0-beta.7`
|
## 10. Path Beyond `1.0.0-beta.8`
|
||||||
|
|
||||||
The beta threshold is now real. The next work should treat `1.0.0-beta` as
|
The beta threshold is now real. The next work should treat `1.0.0-beta` as
|
||||||
the language compatibility-governed baseline, `1.0.0-beta.1` as the first
|
the language compatibility-governed baseline, `1.0.0-beta.1` as the first
|
||||||
@ -514,7 +518,8 @@ stabilization point, and `1.0.0-beta.4` as the first diagnostics usability
|
|||||||
point, and `1.0.0-beta.5` as the first package/workspace discipline point,
|
point, and `1.0.0-beta.5` as the first package/workspace discipline point,
|
||||||
and `1.0.0-beta.6` as the first loopback networking foundation point, and
|
and `1.0.0-beta.6` as the first loopback networking foundation point, and
|
||||||
`1.0.0-beta.7` as the first serialization/data-interchange foundation point,
|
`1.0.0-beta.7` as the first serialization/data-interchange foundation point,
|
||||||
then move deliberately toward stable general-purpose status.
|
and `1.0.0-beta.8` as the first concrete type alias foundation point, then
|
||||||
|
move deliberately toward stable general-purpose status.
|
||||||
|
|
||||||
Recommended sequence:
|
Recommended sequence:
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
(import std.json (quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
(import std.json (quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
||||||
|
|
||||||
|
(type JsonText string)
|
||||||
|
|
||||||
|
(type JsonField string)
|
||||||
|
|
||||||
(fn imported_json_quote_escapes () -> bool
|
(fn imported_json_quote_escapes () -> bool
|
||||||
(if (= (quote_string "slovo") "\"slovo\"")
|
(if (= (quote_string "slovo") "\"slovo\"")
|
||||||
(if (= (quote_string "slo\"vo") "\"slo\\\"vo\"")
|
(if (= (quote_string "slo\"vo") "\"slo\\\"vo\"")
|
||||||
@ -45,15 +49,21 @@
|
|||||||
false)
|
false)
|
||||||
false))
|
false))
|
||||||
|
|
||||||
|
(fn imported_json_name_field () -> JsonField
|
||||||
|
(field_string "name" "slovo"))
|
||||||
|
|
||||||
|
(fn imported_json_object_document () -> JsonText
|
||||||
|
(object3 (imported_json_name_field) (field_i32 "count" 3) (field_bool "ok" true)))
|
||||||
|
|
||||||
(fn imported_json_arrays_objects () -> bool
|
(fn imported_json_arrays_objects () -> bool
|
||||||
(if (= (array0) "[]")
|
(if (= (array0) "[]")
|
||||||
(if (= (array1 (quote_string "a")) "[\"a\"]")
|
(if (= (array1 (quote_string "a")) "[\"a\"]")
|
||||||
(if (= (array2 (quote_string "a") (i32_value 7)) "[\"a\",7]")
|
(if (= (array2 (quote_string "a") (i32_value 7)) "[\"a\",7]")
|
||||||
(if (= (array3 (quote_string "a") (i32_value 7) (bool_value true)) "[\"a\",7,true]")
|
(if (= (array3 (quote_string "a") (i32_value 7) (bool_value true)) "[\"a\",7,true]")
|
||||||
(if (= (object0) "{}")
|
(if (= (object0) "{}")
|
||||||
(if (= (object1 (field_string "name" "slovo")) "{\"name\":\"slovo\"}")
|
(if (= (object1 (imported_json_name_field)) "{\"name\":\"slovo\"}")
|
||||||
(if (= (object2 (field_string "name" "slovo") (field_i32 "count" 3)) "{\"name\":\"slovo\",\"count\":3}")
|
(if (= (object2 (imported_json_name_field) (field_i32 "count" 3)) "{\"name\":\"slovo\",\"count\":3}")
|
||||||
(= (object3 (field_string "name" "slovo") (field_i32 "count" 3) (field_bool "ok" true)) "{\"name\":\"slovo\",\"count\":3,\"ok\":true}")
|
(= (imported_json_object_document) "{\"name\":\"slovo\",\"count\":3,\"ok\":true}")
|
||||||
false)
|
false)
|
||||||
false)
|
false)
|
||||||
false)
|
false)
|
||||||
|
|||||||
@ -1,78 +1,82 @@
|
|||||||
(module json (export quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
(module json (export quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
||||||
|
|
||||||
(fn quote_string ((value string)) -> string
|
(type JsonText string)
|
||||||
|
|
||||||
|
(type JsonField string)
|
||||||
|
|
||||||
|
(fn quote_string ((value string)) -> JsonText
|
||||||
(std.json.quote_string value))
|
(std.json.quote_string value))
|
||||||
|
|
||||||
(fn null_value () -> string
|
(fn null_value () -> JsonText
|
||||||
"null")
|
"null")
|
||||||
|
|
||||||
(fn bool_value ((value bool)) -> string
|
(fn bool_value ((value bool)) -> JsonText
|
||||||
(if value
|
(if value
|
||||||
"true"
|
"true"
|
||||||
"false"))
|
"false"))
|
||||||
|
|
||||||
(fn i32_value ((value i32)) -> string
|
(fn i32_value ((value i32)) -> JsonText
|
||||||
(std.num.i32_to_string value))
|
(std.num.i32_to_string value))
|
||||||
|
|
||||||
(fn u32_value ((value u32)) -> string
|
(fn u32_value ((value u32)) -> JsonText
|
||||||
(std.num.u32_to_string value))
|
(std.num.u32_to_string value))
|
||||||
|
|
||||||
(fn i64_value ((value i64)) -> string
|
(fn i64_value ((value i64)) -> JsonText
|
||||||
(std.num.i64_to_string value))
|
(std.num.i64_to_string value))
|
||||||
|
|
||||||
(fn u64_value ((value u64)) -> string
|
(fn u64_value ((value u64)) -> JsonText
|
||||||
(std.num.u64_to_string value))
|
(std.num.u64_to_string value))
|
||||||
|
|
||||||
(fn f64_value ((value f64)) -> string
|
(fn f64_value ((value f64)) -> JsonText
|
||||||
(std.num.f64_to_string value))
|
(std.num.f64_to_string value))
|
||||||
|
|
||||||
(fn field_fragment ((name string) (encoded_value string)) -> string
|
(fn field_fragment ((name string) (encoded_value JsonText)) -> JsonField
|
||||||
(std.string.concat (std.string.concat (quote_string name) ":") encoded_value))
|
(std.string.concat (std.string.concat (quote_string name) ":") encoded_value))
|
||||||
|
|
||||||
(fn field_string ((name string) (value string)) -> string
|
(fn field_string ((name string) (value string)) -> JsonField
|
||||||
(field_fragment name (quote_string value)))
|
(field_fragment name (quote_string value)))
|
||||||
|
|
||||||
(fn field_bool ((name string) (value bool)) -> string
|
(fn field_bool ((name string) (value bool)) -> JsonField
|
||||||
(field_fragment name (bool_value value)))
|
(field_fragment name (bool_value value)))
|
||||||
|
|
||||||
(fn field_i32 ((name string) (value i32)) -> string
|
(fn field_i32 ((name string) (value i32)) -> JsonField
|
||||||
(field_fragment name (i32_value value)))
|
(field_fragment name (i32_value value)))
|
||||||
|
|
||||||
(fn field_u32 ((name string) (value u32)) -> string
|
(fn field_u32 ((name string) (value u32)) -> JsonField
|
||||||
(field_fragment name (u32_value value)))
|
(field_fragment name (u32_value value)))
|
||||||
|
|
||||||
(fn field_i64 ((name string) (value i64)) -> string
|
(fn field_i64 ((name string) (value i64)) -> JsonField
|
||||||
(field_fragment name (i64_value value)))
|
(field_fragment name (i64_value value)))
|
||||||
|
|
||||||
(fn field_u64 ((name string) (value u64)) -> string
|
(fn field_u64 ((name string) (value u64)) -> JsonField
|
||||||
(field_fragment name (u64_value value)))
|
(field_fragment name (u64_value value)))
|
||||||
|
|
||||||
(fn field_f64 ((name string) (value f64)) -> string
|
(fn field_f64 ((name string) (value f64)) -> JsonField
|
||||||
(field_fragment name (f64_value value)))
|
(field_fragment name (f64_value value)))
|
||||||
|
|
||||||
(fn field_null ((name string)) -> string
|
(fn field_null ((name string)) -> JsonField
|
||||||
(field_fragment name (null_value)))
|
(field_fragment name (null_value)))
|
||||||
|
|
||||||
(fn array0 () -> string
|
(fn array0 () -> JsonText
|
||||||
"[]")
|
"[]")
|
||||||
|
|
||||||
(fn array1 ((first string)) -> string
|
(fn array1 ((first JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat "[" first) "]"))
|
(std.string.concat (std.string.concat "[" first) "]"))
|
||||||
|
|
||||||
(fn array2 ((first string) (second string)) -> string
|
(fn array2 ((first JsonText) (second JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) "]"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) "]"))
|
||||||
|
|
||||||
(fn array3 ((first string) (second string) (third string)) -> string
|
(fn array3 ((first JsonText) (second JsonText) (third JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) ",") third) "]"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) ",") third) "]"))
|
||||||
|
|
||||||
(fn object0 () -> string
|
(fn object0 () -> JsonText
|
||||||
"{}")
|
"{}")
|
||||||
|
|
||||||
(fn object1 ((first string)) -> string
|
(fn object1 ((first JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat "{" first) "}"))
|
(std.string.concat (std.string.concat "{" first) "}"))
|
||||||
|
|
||||||
(fn object2 ((first string) (second string)) -> string
|
(fn object2 ((first JsonField) (second JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) "}"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) "}"))
|
||||||
|
|
||||||
(fn object3 ((first string) (second string) (third string)) -> string
|
(fn object3 ((first JsonField) (second JsonField) (third JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) ",") third) "}"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) ",") third) "}"))
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
(import json (quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
(import json (quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
||||||
|
|
||||||
|
(type JsonText string)
|
||||||
|
|
||||||
|
(type JsonField string)
|
||||||
|
|
||||||
(fn imported_json_quote_escapes () -> bool
|
(fn imported_json_quote_escapes () -> bool
|
||||||
(if (= (quote_string "slovo") "\"slovo\"")
|
(if (= (quote_string "slovo") "\"slovo\"")
|
||||||
(if (= (quote_string "slo\"vo") "\"slo\\\"vo\"")
|
(if (= (quote_string "slo\"vo") "\"slo\\\"vo\"")
|
||||||
@ -45,15 +49,21 @@
|
|||||||
false)
|
false)
|
||||||
false))
|
false))
|
||||||
|
|
||||||
|
(fn imported_json_name_field () -> JsonField
|
||||||
|
(field_string "name" "slovo"))
|
||||||
|
|
||||||
|
(fn imported_json_object_document () -> JsonText
|
||||||
|
(object3 (imported_json_name_field) (field_i32 "count" 3) (field_bool "ok" true)))
|
||||||
|
|
||||||
(fn imported_json_arrays_objects () -> bool
|
(fn imported_json_arrays_objects () -> bool
|
||||||
(if (= (array0) "[]")
|
(if (= (array0) "[]")
|
||||||
(if (= (array1 (quote_string "a")) "[\"a\"]")
|
(if (= (array1 (quote_string "a")) "[\"a\"]")
|
||||||
(if (= (array2 (quote_string "a") (i32_value 7)) "[\"a\",7]")
|
(if (= (array2 (quote_string "a") (i32_value 7)) "[\"a\",7]")
|
||||||
(if (= (array3 (quote_string "a") (i32_value 7) (bool_value true)) "[\"a\",7,true]")
|
(if (= (array3 (quote_string "a") (i32_value 7) (bool_value true)) "[\"a\",7,true]")
|
||||||
(if (= (object0) "{}")
|
(if (= (object0) "{}")
|
||||||
(if (= (object1 (field_string "name" "slovo")) "{\"name\":\"slovo\"}")
|
(if (= (object1 (imported_json_name_field)) "{\"name\":\"slovo\"}")
|
||||||
(if (= (object2 (field_string "name" "slovo") (field_i32 "count" 3)) "{\"name\":\"slovo\",\"count\":3}")
|
(if (= (object2 (imported_json_name_field) (field_i32 "count" 3)) "{\"name\":\"slovo\",\"count\":3}")
|
||||||
(= (object3 (field_string "name" "slovo") (field_i32 "count" 3) (field_bool "ok" true)) "{\"name\":\"slovo\",\"count\":3,\"ok\":true}")
|
(= (imported_json_object_document) "{\"name\":\"slovo\",\"count\":3,\"ok\":true}")
|
||||||
false)
|
false)
|
||||||
false)
|
false)
|
||||||
false)
|
false)
|
||||||
|
|||||||
@ -35,7 +35,10 @@ prefix/suffix helper scopes; `1.0.0-beta.6` networking foundation work releases
|
|||||||
`std/net.slo` as an experimental loopback TCP facade over matching
|
`std/net.slo` as an experimental loopback TCP facade over matching
|
||||||
compiler-known runtime calls; `1.0.0-beta.7` serialization work releases
|
compiler-known runtime calls; `1.0.0-beta.7` serialization work releases
|
||||||
`std/json.slo` as an experimental JSON text-construction facade over
|
`std/json.slo` as an experimental JSON text-construction facade over
|
||||||
`std.json.quote_string` and existing string/number helpers.
|
`std.json.quote_string` and existing string/number helpers; the
|
||||||
|
`1.0.0-beta.8` concrete type alias target keeps that same helper surface while
|
||||||
|
using local `JsonText` and `JsonField` aliases as transparent names for
|
||||||
|
`string` JSON fragments.
|
||||||
|
|
||||||
This directory is the source home for staged standard library modules and
|
This directory is the source home for staged standard library modules and
|
||||||
examples. exp-44 lets project-mode source explicitly import `std/math.slo` as
|
examples. exp-44 lets project-mode source explicitly import `std/math.slo` as
|
||||||
@ -99,6 +102,11 @@ text-construction facade over `std.json.quote_string`, `std.string.concat`,
|
|||||||
and the current `std.num.*_to_string` helpers, while leaving JSON parsing,
|
and the current `std.num.*_to_string` helpers, while leaving JSON parsing,
|
||||||
recursive JSON values, maps/sets, streaming encoders, schema validation,
|
recursive JSON values, maps/sets, streaming encoders, schema validation,
|
||||||
Unicode normalization, and stable text encoding policy deferred;
|
Unicode normalization, and stable text encoding policy deferred;
|
||||||
|
`1.0.0-beta.8` targets top-level concrete type aliases in source facades and
|
||||||
|
uses `JsonText` / `JsonField` only as local transparent aliases inside
|
||||||
|
`std/json.slo` and matching local JSON fixtures. These aliases are not exported
|
||||||
|
standard-library names and do not add runtime helpers, generic aliases,
|
||||||
|
parameterized aliases, maps/sets, or ABI/layout guarantees;
|
||||||
exp-76 extends project-mode source search to `std/vec_i32.slo`, a concrete
|
exp-76 extends project-mode source search to `std/vec_i32.slo`, a concrete
|
||||||
source-authored collection facade over the current promoted `std.vec.i32`
|
source-authored collection facade over the current promoted `std.vec.i32`
|
||||||
runtime family; exp-77 extends that facade with concrete option-returning
|
runtime family; exp-77 extends that facade with concrete option-returning
|
||||||
|
|||||||
@ -1,78 +1,82 @@
|
|||||||
(module json (export quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
(module json (export quote_string null_value bool_value i32_value u32_value i64_value u64_value f64_value field_string field_bool field_i32 field_u32 field_i64 field_u64 field_f64 field_null array0 array1 array2 array3 object0 object1 object2 object3))
|
||||||
|
|
||||||
(fn quote_string ((value string)) -> string
|
(type JsonText string)
|
||||||
|
|
||||||
|
(type JsonField string)
|
||||||
|
|
||||||
|
(fn quote_string ((value string)) -> JsonText
|
||||||
(std.json.quote_string value))
|
(std.json.quote_string value))
|
||||||
|
|
||||||
(fn null_value () -> string
|
(fn null_value () -> JsonText
|
||||||
"null")
|
"null")
|
||||||
|
|
||||||
(fn bool_value ((value bool)) -> string
|
(fn bool_value ((value bool)) -> JsonText
|
||||||
(if value
|
(if value
|
||||||
"true"
|
"true"
|
||||||
"false"))
|
"false"))
|
||||||
|
|
||||||
(fn i32_value ((value i32)) -> string
|
(fn i32_value ((value i32)) -> JsonText
|
||||||
(std.num.i32_to_string value))
|
(std.num.i32_to_string value))
|
||||||
|
|
||||||
(fn u32_value ((value u32)) -> string
|
(fn u32_value ((value u32)) -> JsonText
|
||||||
(std.num.u32_to_string value))
|
(std.num.u32_to_string value))
|
||||||
|
|
||||||
(fn i64_value ((value i64)) -> string
|
(fn i64_value ((value i64)) -> JsonText
|
||||||
(std.num.i64_to_string value))
|
(std.num.i64_to_string value))
|
||||||
|
|
||||||
(fn u64_value ((value u64)) -> string
|
(fn u64_value ((value u64)) -> JsonText
|
||||||
(std.num.u64_to_string value))
|
(std.num.u64_to_string value))
|
||||||
|
|
||||||
(fn f64_value ((value f64)) -> string
|
(fn f64_value ((value f64)) -> JsonText
|
||||||
(std.num.f64_to_string value))
|
(std.num.f64_to_string value))
|
||||||
|
|
||||||
(fn field_fragment ((name string) (encoded_value string)) -> string
|
(fn field_fragment ((name string) (encoded_value JsonText)) -> JsonField
|
||||||
(std.string.concat (std.string.concat (quote_string name) ":") encoded_value))
|
(std.string.concat (std.string.concat (quote_string name) ":") encoded_value))
|
||||||
|
|
||||||
(fn field_string ((name string) (value string)) -> string
|
(fn field_string ((name string) (value string)) -> JsonField
|
||||||
(field_fragment name (quote_string value)))
|
(field_fragment name (quote_string value)))
|
||||||
|
|
||||||
(fn field_bool ((name string) (value bool)) -> string
|
(fn field_bool ((name string) (value bool)) -> JsonField
|
||||||
(field_fragment name (bool_value value)))
|
(field_fragment name (bool_value value)))
|
||||||
|
|
||||||
(fn field_i32 ((name string) (value i32)) -> string
|
(fn field_i32 ((name string) (value i32)) -> JsonField
|
||||||
(field_fragment name (i32_value value)))
|
(field_fragment name (i32_value value)))
|
||||||
|
|
||||||
(fn field_u32 ((name string) (value u32)) -> string
|
(fn field_u32 ((name string) (value u32)) -> JsonField
|
||||||
(field_fragment name (u32_value value)))
|
(field_fragment name (u32_value value)))
|
||||||
|
|
||||||
(fn field_i64 ((name string) (value i64)) -> string
|
(fn field_i64 ((name string) (value i64)) -> JsonField
|
||||||
(field_fragment name (i64_value value)))
|
(field_fragment name (i64_value value)))
|
||||||
|
|
||||||
(fn field_u64 ((name string) (value u64)) -> string
|
(fn field_u64 ((name string) (value u64)) -> JsonField
|
||||||
(field_fragment name (u64_value value)))
|
(field_fragment name (u64_value value)))
|
||||||
|
|
||||||
(fn field_f64 ((name string) (value f64)) -> string
|
(fn field_f64 ((name string) (value f64)) -> JsonField
|
||||||
(field_fragment name (f64_value value)))
|
(field_fragment name (f64_value value)))
|
||||||
|
|
||||||
(fn field_null ((name string)) -> string
|
(fn field_null ((name string)) -> JsonField
|
||||||
(field_fragment name (null_value)))
|
(field_fragment name (null_value)))
|
||||||
|
|
||||||
(fn array0 () -> string
|
(fn array0 () -> JsonText
|
||||||
"[]")
|
"[]")
|
||||||
|
|
||||||
(fn array1 ((first string)) -> string
|
(fn array1 ((first JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat "[" first) "]"))
|
(std.string.concat (std.string.concat "[" first) "]"))
|
||||||
|
|
||||||
(fn array2 ((first string) (second string)) -> string
|
(fn array2 ((first JsonText) (second JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) "]"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) "]"))
|
||||||
|
|
||||||
(fn array3 ((first string) (second string) (third string)) -> string
|
(fn array3 ((first JsonText) (second JsonText) (third JsonText)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) ",") third) "]"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "[" first) ",") second) ",") third) "]"))
|
||||||
|
|
||||||
(fn object0 () -> string
|
(fn object0 () -> JsonText
|
||||||
"{}")
|
"{}")
|
||||||
|
|
||||||
(fn object1 ((first string)) -> string
|
(fn object1 ((first JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat "{" first) "}"))
|
(std.string.concat (std.string.concat "{" first) "}"))
|
||||||
|
|
||||||
(fn object2 ((first string) (second string)) -> string
|
(fn object2 ((first JsonField) (second JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) "}"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) "}"))
|
||||||
|
|
||||||
(fn object3 ((first string) (second string) (third string)) -> string
|
(fn object3 ((first JsonField) (second JsonField) (third JsonField)) -> JsonText
|
||||||
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) ",") third) "}"))
|
(std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat (std.string.concat "{" first) ",") second) ",") third) "}"))
|
||||||
|
|||||||
21
tests/cyclic-type-alias.diag
Normal file
21
tests/cyclic-type-alias.diag
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code TypeAliasCycle)
|
||||||
|
(message "type alias cycle includes `A`")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 22 23)
|
||||||
|
(range 4 7 4 8)
|
||||||
|
)
|
||||||
|
(hint "type aliases must resolve to an existing non-alias concrete type")
|
||||||
|
(related
|
||||||
|
(span
|
||||||
|
(file "<fixture>")
|
||||||
|
(bytes 33 34)
|
||||||
|
(range 5 7 5 8)
|
||||||
|
(message "cycle also includes `B`")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
20
tests/duplicate-type-alias.diag
Normal file
20
tests/duplicate-type-alias.diag
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code DuplicateTypeAlias)
|
||||||
|
(message "duplicate type alias `Count`")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 39 44)
|
||||||
|
(range 5 7 5 12)
|
||||||
|
)
|
||||||
|
(related
|
||||||
|
(span
|
||||||
|
(file "<fixture>")
|
||||||
|
(bytes 22 27)
|
||||||
|
(range 4 7 4 12)
|
||||||
|
(message "original type alias")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
13
tests/malformed-type-alias.diag
Normal file
13
tests/malformed-type-alias.diag
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code MalformedTypeAlias)
|
||||||
|
(message "type alias form must be `(type Alias TargetType)`")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 16 28)
|
||||||
|
(range 4 1 4 13)
|
||||||
|
)
|
||||||
|
(expected "(type Alias TargetType)")
|
||||||
|
)
|
||||||
13
tests/self-type-alias.diag
Normal file
13
tests/self-type-alias.diag
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code SelfTypeAlias)
|
||||||
|
(message "type alias `Count` directly aliases itself")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 28 33)
|
||||||
|
(range 4 13 4 18)
|
||||||
|
)
|
||||||
|
(hint "point the alias at an existing concrete type")
|
||||||
|
)
|
||||||
20
tests/type-alias-name-conflict.diag
Normal file
20
tests/type-alias-name-conflict.diag
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code TypeAliasNameConflict)
|
||||||
|
(message "type alias `Count` conflicts with a struct")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 52 57)
|
||||||
|
(range 7 7 7 12)
|
||||||
|
)
|
||||||
|
(related
|
||||||
|
(span
|
||||||
|
(file "<fixture>")
|
||||||
|
(bytes 24 29)
|
||||||
|
(range 4 9 4 14)
|
||||||
|
(message "struct declaration")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
41
tests/type-alias-value-name-conflict.diag
Normal file
41
tests/type-alias-value-name-conflict.diag
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code TypeAliasNameConflict)
|
||||||
|
(message "type alias `main` conflicts with a function")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 61 65)
|
||||||
|
(range 6 7 6 11)
|
||||||
|
)
|
||||||
|
(related
|
||||||
|
(span
|
||||||
|
(file "<fixture>")
|
||||||
|
(bytes 90 113)
|
||||||
|
(range 10 1 11 5)
|
||||||
|
(message "function declaration")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code TypeAliasNameConflict)
|
||||||
|
(message "type alias `c_add` conflicts with a C import")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 78 83)
|
||||||
|
(range 8 7 8 12)
|
||||||
|
)
|
||||||
|
(related
|
||||||
|
(span
|
||||||
|
(file "<fixture>")
|
||||||
|
(bytes 26 31)
|
||||||
|
(range 4 11 4 16)
|
||||||
|
(message "C import declaration")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
97
tests/type-aliases.checked.lower
Normal file
97
tests/type-aliases.checked.lower
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
program main
|
||||||
|
enum Reading
|
||||||
|
variant Empty
|
||||||
|
variant Value i32
|
||||||
|
struct Measurement
|
||||||
|
field amount: i32
|
||||||
|
field label: string
|
||||||
|
fn bump(value: i32) -> i32
|
||||||
|
binary + : i32
|
||||||
|
var value : i32
|
||||||
|
int 1 : i32
|
||||||
|
fn make_counts(base: i32) -> (array i32 3)
|
||||||
|
array : (array i32 3)
|
||||||
|
var base : i32
|
||||||
|
binary + : i32
|
||||||
|
var base : i32
|
||||||
|
int 1 : i32
|
||||||
|
binary + : i32
|
||||||
|
var base : i32
|
||||||
|
int 2 : i32
|
||||||
|
fn pick_count(values: (array i32 3)) -> i32
|
||||||
|
index : i32
|
||||||
|
var values : (array i32 3)
|
||||||
|
int 1 : i32
|
||||||
|
fn make_measure(amount: i32) -> Measurement
|
||||||
|
construct Measurement : Measurement
|
||||||
|
field amount
|
||||||
|
var amount : i32
|
||||||
|
field label
|
||||||
|
string "ok" : string
|
||||||
|
fn measure_amount(measure: Measurement) -> i32
|
||||||
|
field-access amount : i32
|
||||||
|
var measure : Measurement
|
||||||
|
fn maybe_amount(amount: i32) -> (option i32)
|
||||||
|
some : (option i32)
|
||||||
|
var amount : i32
|
||||||
|
fn maybe_empty() -> (option i32)
|
||||||
|
none : (option i32)
|
||||||
|
fn ok_amount(amount: i32) -> (result i32 i32)
|
||||||
|
ok : (result i32 i32)
|
||||||
|
var amount : i32
|
||||||
|
fn err_amount(code: i32) -> (result i32 i32)
|
||||||
|
err : (result i32 i32)
|
||||||
|
var code : i32
|
||||||
|
fn empty_counts() -> (vec i32)
|
||||||
|
call std.vec.i32.empty : (vec i32)
|
||||||
|
fn reading_value(amount: i32) -> Reading
|
||||||
|
enum-variant Reading.Value #1 payload : Reading
|
||||||
|
var amount : i32
|
||||||
|
fn reading_score(reading: Reading) -> i32
|
||||||
|
match : i32
|
||||||
|
subject
|
||||||
|
var reading : Reading
|
||||||
|
arm Reading.Empty
|
||||||
|
int 0 : i32
|
||||||
|
arm Reading.Value amount
|
||||||
|
var amount : i32
|
||||||
|
fn main() -> i32
|
||||||
|
local let values : unit
|
||||||
|
call make_counts : (array i32 3)
|
||||||
|
int 40 : i32
|
||||||
|
binary + : i32
|
||||||
|
call pick_count : i32
|
||||||
|
var values : (array i32 3)
|
||||||
|
call measure_amount : i32
|
||||||
|
call make_measure : Measurement
|
||||||
|
int 1 : i32
|
||||||
|
test "aliases erase through arrays and structs"
|
||||||
|
binary = : bool
|
||||||
|
call main : i32
|
||||||
|
int 42 : i32
|
||||||
|
test "aliases erase through option result vec and enum"
|
||||||
|
local let maybe : unit
|
||||||
|
call maybe_amount : (option i32)
|
||||||
|
int 7 : i32
|
||||||
|
local let result : unit
|
||||||
|
call ok_amount : (result i32 i32)
|
||||||
|
int 8 : i32
|
||||||
|
if : bool
|
||||||
|
is_some : bool
|
||||||
|
var maybe : (option i32)
|
||||||
|
if : bool
|
||||||
|
is_ok : bool
|
||||||
|
var result : (result i32 i32)
|
||||||
|
if : bool
|
||||||
|
binary = : bool
|
||||||
|
call std.vec.i32.len : i32
|
||||||
|
call empty_counts : (vec i32)
|
||||||
|
int 0 : i32
|
||||||
|
binary = : bool
|
||||||
|
call reading_score : i32
|
||||||
|
call reading_value : Reading
|
||||||
|
int 9 : i32
|
||||||
|
int 9 : i32
|
||||||
|
bool false : bool
|
||||||
|
bool false : bool
|
||||||
|
bool false : bool
|
||||||
79
tests/type-aliases.slo
Normal file
79
tests/type-aliases.slo
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
(module main)
|
||||||
|
|
||||||
|
(type Count i32)
|
||||||
|
|
||||||
|
(type Score Count)
|
||||||
|
|
||||||
|
(type Text string)
|
||||||
|
|
||||||
|
(type Counts (array Count 3))
|
||||||
|
|
||||||
|
(type CountVec (vec Count))
|
||||||
|
|
||||||
|
(type MaybeCount (option Count))
|
||||||
|
|
||||||
|
(type CountResult (result Count Count))
|
||||||
|
|
||||||
|
(struct Measurement
|
||||||
|
(amount Count)
|
||||||
|
(label Text))
|
||||||
|
|
||||||
|
(type Measure Measurement)
|
||||||
|
|
||||||
|
(enum Reading
|
||||||
|
Empty
|
||||||
|
(Value Count))
|
||||||
|
|
||||||
|
(type ReadingAlias Reading)
|
||||||
|
|
||||||
|
(fn bump ((value Count)) -> Score
|
||||||
|
(+ value 1))
|
||||||
|
|
||||||
|
(fn make_counts ((base Count)) -> Counts
|
||||||
|
(array Count base (+ base 1) (+ base 2)))
|
||||||
|
|
||||||
|
(fn pick_count ((values Counts)) -> Count
|
||||||
|
(index values 1))
|
||||||
|
|
||||||
|
(fn make_measure ((amount Count)) -> Measure
|
||||||
|
(Measurement (amount amount) (label "ok")))
|
||||||
|
|
||||||
|
(fn measure_amount ((measure Measure)) -> Count
|
||||||
|
(. measure amount))
|
||||||
|
|
||||||
|
(fn maybe_amount ((amount Count)) -> MaybeCount
|
||||||
|
(some Count amount))
|
||||||
|
|
||||||
|
(fn maybe_empty () -> MaybeCount
|
||||||
|
(none Count))
|
||||||
|
|
||||||
|
(fn ok_amount ((amount Count)) -> CountResult
|
||||||
|
(ok Count Count amount))
|
||||||
|
|
||||||
|
(fn err_amount ((code Count)) -> CountResult
|
||||||
|
(err Count Count code))
|
||||||
|
|
||||||
|
(fn empty_counts () -> CountVec
|
||||||
|
(std.vec.i32.empty))
|
||||||
|
|
||||||
|
(fn reading_value ((amount Count)) -> ReadingAlias
|
||||||
|
(Reading.Value amount))
|
||||||
|
|
||||||
|
(fn reading_score ((reading ReadingAlias)) -> Count
|
||||||
|
(match reading
|
||||||
|
((Reading.Empty)
|
||||||
|
0)
|
||||||
|
((Reading.Value amount)
|
||||||
|
amount)))
|
||||||
|
|
||||||
|
(fn main () -> i32
|
||||||
|
(let values Counts (make_counts 40))
|
||||||
|
(+ (pick_count values) (measure_amount (make_measure 1))))
|
||||||
|
|
||||||
|
(test "aliases erase through arrays and structs"
|
||||||
|
(= (main) 42))
|
||||||
|
|
||||||
|
(test "aliases erase through option result vec and enum"
|
||||||
|
(let maybe MaybeCount (maybe_amount 7))
|
||||||
|
(let result CountResult (ok_amount 8))
|
||||||
|
(and (is_some maybe) (and (is_ok result) (and (= (std.vec.i32.len (empty_counts)) 0) (= (reading_score (reading_value 9)) 9)))))
|
||||||
106
tests/type-aliases.surface.lower
Normal file
106
tests/type-aliases.surface.lower
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
program main
|
||||||
|
type Count = i32
|
||||||
|
type Score = Count
|
||||||
|
type Text = string
|
||||||
|
type Counts = (array Count 3)
|
||||||
|
type CountVec = (vec Count)
|
||||||
|
type MaybeCount = (option Count)
|
||||||
|
type CountResult = (result Count Count)
|
||||||
|
type Measure = Measurement
|
||||||
|
type ReadingAlias = Reading
|
||||||
|
enum Reading
|
||||||
|
variant Empty
|
||||||
|
variant Value Count
|
||||||
|
struct Measurement
|
||||||
|
field amount: Count
|
||||||
|
field label: Text
|
||||||
|
fn bump(value: Count) -> Score
|
||||||
|
binary +
|
||||||
|
var value
|
||||||
|
int 1
|
||||||
|
fn make_counts(base: Count) -> Counts
|
||||||
|
array Count
|
||||||
|
var base
|
||||||
|
binary +
|
||||||
|
var base
|
||||||
|
int 1
|
||||||
|
binary +
|
||||||
|
var base
|
||||||
|
int 2
|
||||||
|
fn pick_count(values: Counts) -> Count
|
||||||
|
index
|
||||||
|
var values
|
||||||
|
int 1
|
||||||
|
fn make_measure(amount: Count) -> Measure
|
||||||
|
construct Measurement
|
||||||
|
field amount
|
||||||
|
var amount
|
||||||
|
field label
|
||||||
|
string "ok"
|
||||||
|
fn measure_amount(measure: Measure) -> Count
|
||||||
|
field-access amount
|
||||||
|
var measure
|
||||||
|
fn maybe_amount(amount: Count) -> MaybeCount
|
||||||
|
some Count
|
||||||
|
var amount
|
||||||
|
fn maybe_empty() -> MaybeCount
|
||||||
|
none Count
|
||||||
|
fn ok_amount(amount: Count) -> CountResult
|
||||||
|
ok Count Count
|
||||||
|
var amount
|
||||||
|
fn err_amount(code: Count) -> CountResult
|
||||||
|
err Count Count
|
||||||
|
var code
|
||||||
|
fn empty_counts() -> CountVec
|
||||||
|
call std.vec.i32.empty
|
||||||
|
fn reading_value(amount: Count) -> ReadingAlias
|
||||||
|
enum-variant Reading.Value
|
||||||
|
var amount
|
||||||
|
fn reading_score(reading: ReadingAlias) -> Count
|
||||||
|
match
|
||||||
|
subject
|
||||||
|
var reading
|
||||||
|
arm Reading.Empty
|
||||||
|
int 0
|
||||||
|
arm Reading.Value amount
|
||||||
|
var amount
|
||||||
|
fn main() -> i32
|
||||||
|
local let values: Counts
|
||||||
|
call make_counts
|
||||||
|
int 40
|
||||||
|
binary +
|
||||||
|
call pick_count
|
||||||
|
var values
|
||||||
|
call measure_amount
|
||||||
|
call make_measure
|
||||||
|
int 1
|
||||||
|
test "aliases erase through arrays and structs"
|
||||||
|
binary =
|
||||||
|
call main
|
||||||
|
int 42
|
||||||
|
test "aliases erase through option result vec and enum"
|
||||||
|
local let maybe: MaybeCount
|
||||||
|
call maybe_amount
|
||||||
|
int 7
|
||||||
|
local let result: CountResult
|
||||||
|
call ok_amount
|
||||||
|
int 8
|
||||||
|
if
|
||||||
|
is_some
|
||||||
|
var maybe
|
||||||
|
if
|
||||||
|
is_ok
|
||||||
|
var result
|
||||||
|
if
|
||||||
|
binary =
|
||||||
|
call std.vec.i32.len
|
||||||
|
call empty_counts
|
||||||
|
int 0
|
||||||
|
binary =
|
||||||
|
call reading_score
|
||||||
|
call reading_value
|
||||||
|
int 9
|
||||||
|
int 9
|
||||||
|
bool false
|
||||||
|
bool false
|
||||||
|
bool false
|
||||||
15
tests/unknown-type-alias-target.diag
Normal file
15
tests/unknown-type-alias-target.diag
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnknownTypeAliasTarget)
|
||||||
|
(message "type alias target `Missing` is not a known concrete type")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 28 35)
|
||||||
|
(range 4 13 4 20)
|
||||||
|
)
|
||||||
|
(expected "built-in type, known struct, known enum, or known type alias")
|
||||||
|
(found "Missing")
|
||||||
|
(hint "declare the target type or alias before checking this alias set")
|
||||||
|
)
|
||||||
79
tests/unsupported-type-alias-target.diag
Normal file
79
tests/unsupported-type-alias-target.diag
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnsupportedTypeAliasTarget)
|
||||||
|
(message "type alias target type is not supported in beta.8")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 50 59)
|
||||||
|
(range 6 14 6 23)
|
||||||
|
)
|
||||||
|
(expected "i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
(found "(ptr i32)")
|
||||||
|
(hint "aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
)
|
||||||
|
|
||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnsupportedTypeAliasTarget)
|
||||||
|
(message "type alias target type is not supported in beta.8")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 132 151)
|
||||||
|
(range 12 17 12 36)
|
||||||
|
)
|
||||||
|
(expected "i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
(found "(result i32 string)")
|
||||||
|
(hint "aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
)
|
||||||
|
|
||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnsupportedTypeAliasTarget)
|
||||||
|
(message "type alias target type is not supported in beta.8")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 77 88)
|
||||||
|
(range 8 16 8 27)
|
||||||
|
)
|
||||||
|
(expected "i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
(found "(slice i32)")
|
||||||
|
(hint "aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
)
|
||||||
|
|
||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnsupportedTypeAliasTarget)
|
||||||
|
(message "type alias target type is not supported in beta.8")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 30 34)
|
||||||
|
(range 4 15 4 19)
|
||||||
|
)
|
||||||
|
(expected "i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
(found "unit")
|
||||||
|
(hint "aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
)
|
||||||
|
|
||||||
|
(diagnostic
|
||||||
|
(schema slovo.diagnostic)
|
||||||
|
(version 1)
|
||||||
|
(severity error)
|
||||||
|
(code UnsupportedTypeAliasTarget)
|
||||||
|
(message "type alias target type is not supported in beta.8")
|
||||||
|
(file "<fixture>")
|
||||||
|
(span
|
||||||
|
(bytes 104 113)
|
||||||
|
(range 10 14 10 23)
|
||||||
|
)
|
||||||
|
(expected "i32, i64, u32, u64, f64, bool, string, known struct, known enum, supported array, supported option, supported result, or supported vec")
|
||||||
|
(found "(vec u32)")
|
||||||
|
(hint "aliases are transparent and may only target concrete types already supported in the target use positions")
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user