Skip to content

Commit bf8ea7e

Browse files
committed
jule: add generic type support to strict type aliases
1 parent 253f9e6 commit bf8ea7e

File tree

8 files changed

+119
-32
lines changed

8 files changed

+119
-32
lines changed

std/jule/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ It is also used by the official reference compiler JuleC and is developed in par
1818

1919
- **(2)** All generic types represented by `sema::TypeAlias` structure, and pushed into symbol table of relevant scope. Appends to symbol table first owner function's (see (1)) generics, then appends function's owner's (structure, so owner function is actually a method) generics if exist.
2020

21-
- **(3)** The `Generics` field of `sema::TypeAlias` structure is stores all generic types that used in evaluation of destination type kind of type alias. This types are deep references. Stores `*T` or `[]T`, but also stores deep usages such as `MyStruct[T]` or `fn(s: MyStruct[[]*T])` types.
21+
- **(3)** The `Generics` field of `sema::TypeAlias` stores generic type declaration of the type alias. They also will be used for the underlying structure type for the generic handling. The `alias` field of the underlying `sema::Struct` will point to the owner `sema::TypeAlias`.
2222

2323
- **(4)** If the `referencer.owner` field of `sema::TypeChecker` structure is type alias which is uses `TypeChecker` instance to build it's own destination type kind. This is the hard reference to owner. Always points to root type alias of this build even in nested type builds. Used to collect generic dependencies (see (3)) and etc. of type aliaes.
2424

std/jule/ast/node.jule

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,14 @@ struct Conditional {
531531

532532
// Type alias declaration.
533533
struct TypeAlias {
534-
Scope: &ScopeTree
535-
Public: bool
536-
Bind: bool
537-
Token: &token::Token
538-
Name: str
539-
Strict: bool
540-
Type: &Expr
534+
Scope: &ScopeTree
535+
Public: bool
536+
Bind: bool
537+
Token: &token::Token
538+
Name: str
539+
Strict: bool
540+
Type: &Expr
541+
Generics: []&Generic
541542
}
542543

543544
// Case of match-case.

std/jule/parser/parser.jule

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,17 @@ impl parser {
293293
ret tad
294294
}
295295
token = tokens[i]
296+
297+
mut genericsTokens := range(&i, token::LBRACK, token::RBRACK, tokens)
298+
if genericsTokens != nil {
299+
tad.Generics = self.buildGenerics(genericsTokens, token)
300+
}
301+
if i >= len(tokens) {
302+
self.pushErr(tokens[i-1], "invalid syntax")
303+
ret tad
304+
}
305+
token = tokens[i]
306+
296307
match token.ID {
297308
| token::COLON:
298309
tad.Strict = true

std/jule/sema/eval.jule

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,13 @@ impl eval {
689689
// But, as far as tested, there is no risk for current implementation.
690690
// Future implementations should pay attention for here.
691691
mut s := kind.(&StructIns)
692+
// Handle generic strict type alias structure instances.
693+
// They are must be instantiated and the source instance is a placeholder.
694+
// Do not mutate it, never. Allocate a new instance, and handle it.
695+
if len(ta.Generics) > 0 {
696+
s = s.Decl.instance()
697+
ret self._evalStruct(s)
698+
}
692699
self.pushReference(s)
693700
v = self._evalStruct(s)
694701
| &Enum:

std/jule/sema/sema.jule

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ impl sema {
703703
// Checks TypeSYm, builds result as Type and collects referred type aliases.
704704
// Skips already checked types.
705705
// See also developer reference (9).
706-
fn checkTypeSymWithRefers(mut &self, mut t: &TypeSym, mut l: Lookup, mut referencer: &referencer): (ok: bool) {
706+
fn checkTypeSymWithRefers(mut &self, mut t: &TypeSym, mut l: Lookup, mut generics: []&TypeAlias, mut referencer: &referencer): (ok: bool) {
707707
mut ta := false // Referencer owner is type alias.
708708
if t.checked() {
709709
match type referencer.owner {
@@ -714,7 +714,7 @@ impl sema {
714714
ret true
715715
}
716716
}
717-
mut tt := self.buildTypeWithRefers(t.Decl, l, nil, referencer)
717+
mut tt := self.buildTypeWithRefers(t.Decl, l, generics, referencer)
718718
if tt == nil || !ta {
719719
t.Type = tt
720720
} else {
@@ -730,7 +730,7 @@ impl sema {
730730
// Checks type and builds result as Type.
731731
// Skips already checked types.
732732
fn checkTypeSym(mut &self, mut t: &TypeSym, mut l: Lookup): bool {
733-
ret self.checkTypeSymWithRefers(t, l, nil)
733+
ret self.checkTypeSymWithRefers(t, l, nil, nil)
734734
}
735735

736736
// Same as the checkTypeSym but takes referencer.
@@ -1027,6 +1027,28 @@ impl sema {
10271027
ret self.basicFuncEnvironment(f, fn|mut sema| algo(f.env.sema, f.env.generics))
10281028
}
10291029

1030+
// Calls algo in the struct's native environment.
1031+
// Errors will be handled.
1032+
// Returns result of algo.
1033+
fn basicStructEnvironment(mut &self, mut s: &StructIns, algo: fn(mut sema: &sema): bool): bool {
1034+
s.fillEnviron()
1035+
1036+
if s.env.file != nil {
1037+
mut old := s.env.sema.getCurrentFile()
1038+
defer { s.env.sema.setCurrentFile(old) }
1039+
s.env.sema.setCurrentFile(s.env.file)
1040+
}
1041+
1042+
ok := algo(s.env.sema)
1043+
1044+
if s.env.sema != self {
1045+
self.errors = append(self.errors, s.env.sema.errors...)
1046+
s.env.sema.errors = nil
1047+
}
1048+
1049+
ret ok
1050+
}
1051+
10301052
fn checkFuncParamKind(mut &self, mut p: &ParamIns) {
10311053
p.Type.Variadic = p.Decl.Variadic
10321054
if p.Decl.Reference {
@@ -1195,7 +1217,7 @@ impl sema {
11951217
ret atc.checkValidity()
11961218
}
11971219

1198-
fn checkTypeAliasDeclKind(mut &self, mut ta: &TypeAlias, mut prev: &referencer, mut l: Lookup): (ok: bool) {
1220+
fn checkTypeAliasDeclKind(mut &self, mut ta: &TypeAlias, mut generics: []&TypeAlias, mut prev: &referencer, mut l: Lookup): (ok: bool) {
11991221
mut old := self.file
12001222
defer {
12011223
self.setCurrentFile(old)
@@ -1216,7 +1238,7 @@ impl sema {
12161238
defer { prev.next = nil }
12171239
}
12181240
referencer.tains = ta.Strict
1219-
ok = self.checkTypeSymWithRefers(ta.TypeSym, l, referencer)
1241+
ok = self.checkTypeSymWithRefers(ta.TypeSym, l, generics, referencer)
12201242
if ok && ta.TypeSym.Type.Array() != nil && ta.TypeSym.Type.Array().Auto {
12211243
self.pushErr(ta.TypeSym.Decl.Token, "auto-sized arrays are not allowed as value type")
12221244
ok = false
@@ -1241,8 +1263,33 @@ impl sema {
12411263
s.Decl.Token = ta.Token
12421264
s.Decl.Public = ta.Public
12431265
ta.TypeSym.Type = &Type{Kind: s}
1266+
if len(ta.Generics) > 0 {
1267+
if ta.Bind {
1268+
self.pushErr(ta.Token, "bind strict type alias cannot have generics")
1269+
ret false
1270+
}
1271+
ok = self.checkDeclGenerics(ta.Generics)
1272+
if !ok {
1273+
ret false
1274+
}
1275+
// Remove instances for generic type alias.
1276+
// This instance will be placeholder for the declaration.
1277+
// Generic type aliases handled like structs.
1278+
s.Decl.Instances = nil
1279+
// Assign type alias generics to underlying structure.
1280+
// This enables generic type analysis and support for the struct type.
1281+
// Also assign the type alias to the struct.
1282+
// See developer reference (3).
1283+
s.Decl.Generics = ta.Generics
1284+
s.Decl.alias = ta
1285+
ret true
1286+
}
1287+
} else if len(ta.Generics) > 0 {
1288+
self.pushErr(ta.Token, "soft type alias cannot have generics")
1289+
self.pushSuggestion("define as strict type alias, like; type @[Generics]: Type", ta.Name)
1290+
ret false
12441291
}
1245-
ok = self.checkTypeAliasDeclKind(ta, prev, l)
1292+
ok = self.checkTypeAliasDeclKind(ta, nil, prev, l)
12461293
if !ok {
12471294
ret false
12481295
}
@@ -2903,18 +2950,20 @@ impl sema {
29032950
self.setCurrentFile(f)
29042951
for (_, mut ta) in self.file.TypeAliases {
29052952
if ta.Strict {
2906-
mut s := ta.TypeSym.Type.Kind.(&StructIns)
2907-
if !s.checked {
2908-
s.checked = true
2909-
ok := self.precheckStructIns(s, nil, nil)
2910-
if !ok {
2911-
ret
2953+
mut s := ta.TypeSym.Type.Kind.(&StructIns).Decl
2954+
for (_, mut ins) in s.Instances {
2955+
if !ins.checked {
2956+
ins.checked = true
2957+
ok := self.precheckStructIns(ins, nil, nil)
2958+
if !ok {
2959+
ret
2960+
}
29122961
}
29132962
}
29142963
// We can check strict structures here. It should be safe
29152964
// as described above; structures will be prechecked if needed.
29162965
// So other unchecked strict type aliases should not be a concern.
2917-
self.checkTypeStruct(s.Decl)
2966+
self.checkTypeStruct(s)
29182967
}
29192968
}
29202969
}

std/jule/sema/struct.jule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct Struct {
5555
Instances: []&StructIns
5656

5757
checked: bool
58+
alias: &TypeAlias // See developer reference (3).
5859
}
5960

6061
impl Struct {

std/jule/sema/sym.jule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ fn buildTypeAlias(mut decl: &ast::TypeAlias): &TypeAlias {
100100
Token: decl.Token,
101101
Name: decl.Name,
102102
TypeSym: buildType(decl.Type),
103+
Generics: decl.Generics,
103104
}
104105
}
105106

std/jule/sema/type.jule

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct TypeAlias {
3030
Token: &token::Token
3131
Name: str
3232
TypeSym: &TypeSym
33-
Generics: []&TypeAlias // See developer reference (1).
33+
Generics: []&ast::Generic // See the developer reference (3).
3434
}
3535

3636
// Kind of type declaration.
@@ -1078,21 +1078,20 @@ impl typeChecker {
10781078
}
10791079
}
10801080

1081-
// Push generic reference to owner type alias.
1082-
// See developer reference (3) and (4) for more information.
1083-
if ta.Generic && self.referencer != nil {
1084-
mut rta, ok := self.referencer.owner.(&TypeAlias)
1085-
if ok {
1086-
rta.Generics = append(rta.Generics, ta)
1087-
}
1088-
}
1089-
10901081
mut tk := &Type{
10911082
Provider: ta.Name,
10921083
Generic: ta.Generic,
10931084
Kind: ta.TypeSym.Type.Kind,
10941085
}
1095-
self.pushReferenceByKind(tk)
1086+
1087+
// Handle generic strict type alias structure instances.
1088+
// They are must be instantiated and the source instance is a placeholder.
1089+
// Do not mutate it, never. Allocate a new instance, and handle it.
1090+
if len(ta.Generics) > 0 {
1091+
tk.Kind = ta.TypeSym.Type.Kind.(&StructIns).Decl.instance()
1092+
} else {
1093+
self.pushReferenceByKind(tk)
1094+
}
10961095
ret tk
10971096
}
10981097

@@ -1158,6 +1157,21 @@ impl typeChecker {
11581157
if self.s != ins.Decl.sema && len(ins.Decl.Generics) > 0 {
11591158
self.s.meta.flags |= semametaGstruct
11601159
}
1160+
1161+
// If instance is generic strict type alias.
1162+
// Resolve the source type using resolved generics.
1163+
if len(ins.Generics) > 0 && ins.Decl.alias != nil {
1164+
ok = self.s.basicStructEnvironment(ins, fn|mut sema| {
1165+
ret unsafe { self.s.checkTypeAliasDeclKind(ins.Decl.alias, ins.env.generics, nil, sema) }
1166+
})
1167+
if !ok {
1168+
ret false
1169+
}
1170+
mut typ := ins.Decl.alias.TypeSym.Type
1171+
ins.Source = ins.Decl.alias.TypeSym.Type
1172+
ins.Decl.alias.TypeSym.Type = typ
1173+
}
1174+
11611175
ok = self.s.precheckStructIns(ins, self.getReferencer(), errorToken)
11621176
if ok && len(ins.Decl.Generics) > 0 {
11631177
// If gstruct collection mode already enabled for this sema,
@@ -1218,6 +1232,9 @@ impl typeChecker {
12181232
if len(s.Generics) == 0 {
12191233
ret false, nil
12201234
}
1235+
if s.alias != nil {
1236+
ret true, s.Generics
1237+
}
12211238
mut sc, mut ok := self.lookup.(&scopeChecker)
12221239
if ok {
12231240
root := sc.getHardRoot()

0 commit comments

Comments
 (0)