Skip to content

Commit 058800c

Browse files
committed
sema: fix struct type analysis and instance collection of recheck strategy
1 parent 80b2437 commit 058800c

File tree

3 files changed

+80
-39
lines changed

3 files changed

+80
-39
lines changed

std/jule/sema/eval.jule

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,7 @@ impl eval {
11001100
v = nil
11011101
ret
11021102
}
1103-
s = self.typeChecker().buildStructInsWithGenerics(s, i)
1103+
s, _ = self.typeChecker().buildStructInsWithGenerics(s, i)
11041104
if s == nil {
11051105
v = nil
11061106
ret
@@ -1112,7 +1112,7 @@ impl eval {
11121112
// If instance is already exist, f will point to exist instantantiation.
11131113
// If recheckNeed is not nil, function will checked with recheck strategy.
11141114
fn checkGenericFunc(mut &self, mut &f: &FuncIns, mut &et: &token::Token,
1115-
mut &model: Expr, mut recheckNeed: []recheckableType): (ok: bool, exist: bool) {
1115+
mut &model: Expr, mut recheckNeed: []&Type): (ok: bool, exist: bool) {
11161116
mut old := f
11171117
if recheckNeed == nil {
11181118
ok, exist = self.s.checkGenericFunc(f, et)
@@ -2144,7 +2144,7 @@ impl eval {
21442144

21452145
// This will be used to collect recheck-needed types if dynamic annotation enabled.
21462146
// See developer reference (15).
2147-
let mut recheckNeed: []recheckableType
2147+
let mut recheckNeed: []&Type
21482148

21492149
if !dynamicAnnotation {
21502150
if !f.reloaded {
@@ -2162,7 +2162,7 @@ impl eval {
21622162
} else {
21632163
// Use non-nil slice for dynamic type annotation.
21642164
// Thus we can use recheck strategy even for zero recheck-need cases.
2165-
recheckNeed = make([]recheckableType, 0, len(f.Params)/2)
2165+
recheckNeed = make([]&Type, 0, len(f.Params)/2)
21662166
if !self.s.buildFuncNonGenericTypes(f, fcac.ignored, recheckNeed) {
21672167
v = nil
21682168
ret

std/jule/sema/sema.jule

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ impl sema {
881881
// - For non-generic type parsed string type kinds.
882882
// - For checking non-generic types.
883883
fn buildNonGenericType(mut &self, mut &ast: &ast::Expr,
884-
mut useGenerics: []&TypeAlias, mut &ignoreGenerics: []&ast::Generic, mut &ignored: []&Type, mut &recheckNeed: []recheckableType): &Type {
884+
mut useGenerics: []&TypeAlias, mut &ignoreGenerics: []&ast::Generic, mut &ignored: []&Type, mut &recheckNeed: []&Type): &Type {
885885
mut tc := &typeChecker{
886886
s: self,
887887
rootLookup: self,
@@ -894,7 +894,7 @@ impl sema {
894894
ret tc.checkDecl(ast)
895895
}
896896

897-
fn buildFuncNonGenericTypes(mut &self, mut f: &FuncIns, mut &ignored: []&Type, mut &recheckNeed: []recheckableType): (ok: bool) {
897+
fn buildFuncNonGenericTypes(mut &self, mut f: &FuncIns, mut &ignored: []&Type, mut &recheckNeed: []&Type): (ok: bool) {
898898
// Check with funcEnvironment, we need to generics of owner, if any.
899899
// If owner generics are ignored, we cannot resolve them with type inference.
900900
// Resolve owner generics in time, only ignore generics of the function.
@@ -2318,7 +2318,7 @@ impl sema {
23182318
ret nil
23192319
}
23202320

2321-
fn recheckType(mut &self, mut t: recheckableType, mut &errorToken: &token::Token,
2321+
fn recheckType(mut &self, mut t: &Type, mut &errorToken: &token::Token,
23222322
mut &refers: &ReferenceStack): (ok: bool) {
23232323
mut tc := &typeChecker{
23242324
s: self,
@@ -2333,7 +2333,7 @@ impl sema {
23332333
// but checks the function with the recheck strategy.
23342334
// See developer reference (15).
23352335
fn checkGenericFuncRecheck(mut &self, mut &f: &FuncIns, mut &et: &token::Token,
2336-
mut recheckNeed: []recheckableType): (ok: bool, exist: bool) {
2336+
mut recheckNeed: []&Type): (ok: bool, exist: bool) {
23372337
// If len(recheckNeed)>0, check the types before handling function.
23382338
if len(recheckNeed) > 0 {
23392339
ok = true

std/jule/sema/type.jule

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -711,10 +711,6 @@ struct referencer {
711711
next: &referencer // Next referencer.
712712
}
713713

714-
enum recheckableType: type {
715-
&StructIns,
716-
}
717-
718714
// Cycle state flags.
719715
const (
720716
cycleErrDisable = 1 << iota // Disable error logging for cycle analysis.
@@ -764,7 +760,7 @@ struct typeChecker {
764760
ignoredGenerics: *[]&Type
765761

766762
// See developer reference (15).
767-
recheckNeed: *[]recheckableType
763+
recheckNeed: *[]&Type
768764

769765
// This generics used as type alias for real kind.
770766
useGenerics: []&TypeAlias
@@ -1187,7 +1183,7 @@ impl typeChecker {
11871183

11881184
// Builds generic types and associates with struct s.
11891185
// Returns struct s if no error occurred, otherwise returns nil.
1190-
fn getStructInsWithGenerics(mut self, mut s: &StructIns, mut indx: &ast::IndexExpr): &StructIns {
1186+
fn getStructInsWithGenerics(mut self, mut s: &StructIns, mut indx: &ast::IndexExpr): (&StructIns, recheckNeeded: bool) {
11911187
inscatch, letGenerics := self.inscatchNeeded(s.Decl)
11921188

11931189
// Save configuration.
@@ -1207,8 +1203,7 @@ impl typeChecker {
12071203
defer { self.lookup = lookup }
12081204

12091205
// Whether the instance pushed to recheck stack.
1210-
mut recheck := false
1211-
mut &_recheck := recheck // For closure.
1206+
mut &_recheck := recheckNeeded // For closure.
12121207

12131208
pushGenericToIns := fn(mut typDecl: &ast::Expr): bool {
12141209
mut typ := unsafe { self.checkDecl(typDecl) }
@@ -1242,7 +1237,6 @@ impl typeChecker {
12421237
prim := typ.Prim()
12431238
if prim != nil && unsafe { self.isIgnoredGeneric(prim.Kind) } {
12441239
unsafe { _recheck = true }
1245-
unsafe { *self.recheckNeed = append(*self.recheckNeed, s) }
12461240
}
12471241
}
12481242
s.Generics = append(s.Generics, &InsGeneric{Type: typ})
@@ -1257,28 +1251,54 @@ impl typeChecker {
12571251
for (_, mut typDecl) in tup.X {
12581252
ok := pushGenericToIns(typDecl)
12591253
if !ok {
1260-
ret nil
1254+
ret nil, false
12611255
}
12621256
}
12631257
|:
12641258
ok := pushGenericToIns(indx.Index)
12651259
if !ok {
1266-
ret nil
1260+
ret nil, false
12671261
}
12681262
}
1269-
ret s
1263+
ret s, recheckNeeded
12701264
}
12711265

1272-
fn buildStructInsWithGenerics(mut self, mut s: &StructIns, mut indx: &ast::IndexExpr): &StructIns {
1273-
s = self.getStructInsWithGenerics(s, indx)
1266+
fn buildStructInsWithGenerics(mut self, mut s: &StructIns, mut indx: &ast::IndexExpr): (&StructIns, recheckNeeded: bool) {
1267+
s, recheckNeeded = self.getStructInsWithGenerics(s, indx)
12741268
if s == nil {
1275-
ret nil
1269+
ret nil, false
12761270
}
12771271
mut ok := self.s.checkGenericQuantity(len(s.Decl.Generics), len(s.Generics), indx.X.Token)
12781272
if !ok {
1279-
ret nil
1273+
ret nil, false
1274+
}
1275+
// If recheck needed for the structure, do not build instance.
1276+
// Avoid appending to collection, otherwise we may have duplicate instances.
1277+
// For example:
1278+
//
1279+
// consider this function:
1280+
// fn Foo(): Bar[T]
1281+
//
1282+
// consider this code:
1283+
// x := int(10)
1284+
// Foo(x)
1285+
// Foo(x)
1286+
//
1287+
// In this case, Foo called with type inferred generics.
1288+
// We build for the non-generic types first.
1289+
// If we append instance for Bar[T], we will have duplicate instances.
1290+
// Because Bar[T] will resolved as Bar[int] in the first call.
1291+
// The second will append Bar[T] to collection again, because there is no Bar[T].
1292+
// Then the new Bar[T] will resolved as Bar[int], as a result we have two Bar[int].
1293+
// This may also cause some conflicts, since lookup returns the first match.
1294+
//
1295+
// As explained above, we should not append instances into collection here.
1296+
// Return with s directly, and report recheck needed.
1297+
// Append instance into collection when analysis sent type to recheck.
1298+
if recheckNeeded {
1299+
ret s, recheckNeeded
12801300
}
1281-
ret self.buildStructIns(s, indx.X.Token)
1301+
ret self.buildStructIns(s, indx.X.Token), false
12821302
}
12831303

12841304
fn buildStructIns(mut self, mut s: &StructIns, mut errToken: &token::Token): &StructIns {
@@ -1751,25 +1771,26 @@ impl typeChecker {
17511771
ret kind
17521772
}
17531773

1754-
fn buildIndex(mut self, mut indx: &ast::IndexExpr): Kind {
1774+
fn buildIndex(mut self, mut indx: &ast::IndexExpr): (Kind, recheckNeeded: bool) {
17551775
mut typ := self.build(indx.X.Data)
17561776
if typ == nil {
1757-
ret nil
1777+
ret nil, false
17581778
}
17591779
mut s := typ.SoftStruct()
17601780
if self.selection {
17611781
self.pushErr(indx.X.Token, log::GenericsNotAllowed)
1762-
ret nil
1782+
ret nil, false
17631783
}
17641784
if s == nil || s.Source != nil || len(s.Generics) > 0 {
17651785
self.pushErr(indx.X.Token, log::TypeNotSupportsGenerics, typ.Str())
1766-
ret nil
1786+
ret nil, false
17671787
}
17681788
ret self.buildStructInsWithGenerics(s, indx)
17691789
}
17701790

17711791
fn build(mut self, mut declKind: ast::ExprData): &Type {
17721792
let mut kind: Kind = nil
1793+
let mut recheckNeeded: bool = false
17731794
match type declKind {
17741795
| &ast::RangeExpr:
17751796
mut r := declKind.(&ast::RangeExpr)
@@ -1833,7 +1854,7 @@ impl typeChecker {
18331854
kind = t
18341855
}
18351856
| &ast::IndexExpr:
1836-
mut t := self.buildIndex(declKind.(&ast::IndexExpr))
1857+
mut t, (recheckNeeded) := self.buildIndex(declKind.(&ast::IndexExpr))
18371858
if t != nil {
18381859
kind = t
18391860
}
@@ -1844,19 +1865,24 @@ impl typeChecker {
18441865
if kind == nil {
18451866
ret nil
18461867
}
1868+
mut typ := (&Type)(nil)
18471869
match type kind {
18481870
| &Type:
1849-
ret kind.(&Type)
1871+
typ = kind.(&Type)
18501872
|:
1851-
mut tk := &Type{Kind: kind}
1873+
typ = &Type{Kind: kind}
18521874
if self.ignoredGenerics != nil {
18531875
_, prim := kind.(&Prim)
18541876
if prim {
1855-
unsafe { *self.ignoredGenerics = append(*self.ignoredGenerics, tk) }
1877+
unsafe { *self.ignoredGenerics = append(*self.ignoredGenerics, typ) }
18561878
}
18571879
}
1858-
ret tk
18591880
}
1881+
// Recheck needed, append type into collection.
1882+
if recheckNeeded {
1883+
unsafe { *self.recheckNeed = append(*self.recheckNeed, typ) }
1884+
}
1885+
ret typ
18601886
}
18611887

18621888
fn checkDecl(mut self, mut &decl: &ast::Expr): &Type {
@@ -1900,10 +1926,10 @@ impl typeChecker {
19001926

19011927
// Rechecks the recheckable type.
19021928
// See developer reference (15).
1903-
fn recheck(mut self, mut &t: recheckableType, mut &errorToken: &token::Token): (ok: bool) {
1904-
match type t {
1905-
| &StructIns:
1906-
mut s := t.(&StructIns)
1929+
fn recheck(mut self, mut &t: &Type, mut &errorToken: &token::Token): (ok: bool) {
1930+
match {
1931+
| t.SoftStruct() != nil:
1932+
mut s := t.SoftStruct()
19071933
s.checked = false
19081934

19091935
// We need to hard-check methods, if it have any method.
@@ -1920,7 +1946,22 @@ impl typeChecker {
19201946
}
19211947
}
19221948

1923-
ok = self._fromStructIns(s, errorToken)
1949+
// Recheck needed types will not be appended.
1950+
// So append instance to the collection. The buildStructIns will do it.
1951+
// Now, the instance s have resolved types completely.
1952+
// There is no risk for duplicated instances. Time to append.
1953+
//
1954+
// The buildStructIns returns common instance to avoid using duplicated instances.
1955+
// So the returned instance may be already existing or the same instance we pass.
1956+
// In both cases, use the result to make sure we have common instance.
1957+
s = self.buildStructIns(s, errorToken)
1958+
// The result instance is not nil, so analysis were successful.
1959+
// Update t.Kind with the instance s to make sure using common instance.
1960+
// Because all types must be point to the same common instance.
1961+
if s != nil {
1962+
ok = true
1963+
t.Kind = s
1964+
}
19241965
}
19251966
ret
19261967
}

0 commit comments

Comments
 (0)