From 0b50e5984e5444701c328bee547ee950828668bb Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:59:20 +0300 Subject: [PATCH 01/11] sacrifice "tgenericshardcases" for working statics --- compiler/semexprs.nim | 15 ++++-- compiler/seminst.nim | 3 +- compiler/semtypes.nim | 7 ++- compiler/semtypinst.nim | 2 +- compiler/types.nim | 5 +- tests/generics/tgenerics_various.nim | 6 +-- tests/statictypes/tgenericobjectarray.nim | 47 ++++++++++++++++++ tests/statictypes/tstaticgenericparam.nim | 60 +++++++++++++++++++++++ 8 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 tests/statictypes/tgenericobjectarray.nim create mode 100644 tests/statictypes/tstaticgenericparam.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 53c0790fe6d4..79b01f44f162 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -313,7 +313,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool = let s = qualifiedLookUp(c, n, {}) result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" -proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -338,7 +338,14 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = return evaluated else: targetType = targetType.base - of tyAnything, tyUntyped, tyTyped: + of tyUntyped: + if efDetermineType in flags: + result = n[1] + result.typ = makeTypeFromExpr(c, result.copyTree) + return + else: + localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) + of tyAnything, tyTyped: localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) else: discard @@ -1071,7 +1078,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct}) if t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags, expectedType) - return semConv(c, n) + return semConv(c, n, flags) let nOrig = n.copyTree semOpAux(c, n) @@ -3114,7 +3121,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: - result = semConv(c, n, expectedType) + result = semConv(c, n, flags, expectedType) elif ambig and n.len == 1: errorUseQualifier(c, n.info, s) elif n.len == 1: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 3afbedec7f45..b4b5ad0a2371 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,8 +388,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - if c.inGenericContext == 0: - instantiateBody(c, n, fn.typ.n, result, fn) + instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 14ad72fcaeed..20737475b914 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -277,7 +277,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen) let hasUnknownTypes = c.inGenericContext > 0 and - rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr + (rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr) if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): @@ -380,7 +380,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = let indx = semArrayIndex(c, n[1]) var indxB = indx if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB) - if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: + if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and + tfUnresolved notin indxB.flags: if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}: discard elif not isOrdinalType(indxB): @@ -394,6 +395,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # bug #6682: Do not propagate initialization requirements etc for the # index type: rawAddSonNoPropagationOfTypeFlags(result, indx) + if tfUnresolved in indxB.flags: + result.flags.incl tfUnresolved addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errArrayExpectsTwoTypeParams) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 50dba515d4ed..6653fd750342 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -250,7 +250,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = result = newNodeI(nkRecList, n.info) of nkStaticExpr: var n = prepareNode(cl, n) - n = reResolveCallsWithTypedescParams(cl, n) + #n = reResolveCallsWithTypedescParams(cl, n) result = if cl.allowMetaTypes: n else: cl.c.semExpr(cl.c, n) if not cl.allowMetaTypes: diff --git a/compiler/types.nim b/compiler/types.nim index 353c445a6d6b..4a75143705da 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -209,6 +209,8 @@ proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter, # a leaf result = iterOverTypeAux(marker, n.typ, iter, closure) else: + result = iterOverTypeAux(marker, n.typ, iter, closure) + if result: return for i in 0.. Date: Sat, 10 Jun 2023 20:39:13 +0300 Subject: [PATCH 02/11] legacy switch for CI, maybe experimental later --- compiler/options.nim | 1 + compiler/semexprs.nim | 8 +- compiler/seminst.nim | 4 +- compiler/semtypes.nim | 10 ++- compiler/semtypinst.nim | 5 +- testament/important_packages.nim | 2 +- .../generics/tuninstantiatedgenericcalls.nim | 82 +++++++++++++++++++ tests/statictypes/tstaticgenericparam.nim | 1 + 8 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 tests/generics/tuninstantiatedgenericcalls.nim diff --git a/compiler/options.nim b/compiler/options.nim index 7e9e37384fbe..1cefcb6bd082 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -233,6 +233,7 @@ type laxEffects ## Lax effects system prior to Nim 2.0. verboseTypeMismatch + uninstantiatedGenericCalls SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 79b01f44f162..cf9553c38922 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -313,7 +313,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool = let s = qualifiedLookUp(c, n, {}) result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" -proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode = +proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -339,7 +339,7 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType else: targetType = targetType.base of tyUntyped: - if efDetermineType in flags: + if c.inGenericContext > 0: result = n[1] result.typ = makeTypeFromExpr(c, result.copyTree) return @@ -1078,7 +1078,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct}) if t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags, expectedType) - return semConv(c, n, flags) + return semConv(c, n) let nOrig = n.copyTree semOpAux(c, n) @@ -3121,7 +3121,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: - result = semConv(c, n, flags, expectedType) + result = semConv(c, n, expectedType) elif ambig and n.len == 1: errorUseQualifier(c, n.info, s) elif n.len == 1: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b4b5ad0a2371..f520afce1224 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,7 +388,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - instantiateBody(c, n, fn.typ.n, result, fn) + if not (uninstantiatedGenericCalls in c.config.legacyFeatures and + c.inGenericContext != 0): + instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 20737475b914..39957546cae5 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -269,6 +269,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, "range is empty") var range: array[2, PNode] + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types range[0] = semExprWithType(c, n[1], {efDetermineType}) range[1] = semExprWithType(c, n[2], {efDetermineType}) @@ -337,6 +339,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType = elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<": result = errorType(c) else: + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types let e = semExprWithType(c, n, {efDetermineType}) if e.typ.kind == tyFromExpr: result = makeRangeWithStaticExpr(c, e.typ.n) @@ -357,7 +361,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) # This is an int returning call, depending on an - # yet unknown generic param (see tgenericshardcases). + # yet unknown generic param (see tuninstantiatedgenericcalls). # We are going to construct a range type that will be # properly filled-out in semtypinst (see how tyStaticExpr # is handled there). @@ -395,8 +399,6 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # bug #6682: Do not propagate initialization requirements etc for the # index type: rawAddSonNoPropagationOfTypeFlags(result, indx) - if tfUnresolved in indxB.flags: - result.flags.incl tfUnresolved addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errArrayExpectsTwoTypeParams) @@ -740,6 +742,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, if e.kind != nkIntLit: discard "don't report followup error" elif e.intVal != 0 and branch == nil: branch = it[1] else: + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types it[0] = forceBool(c, semExprWithType(c, it[0])) of nkElse: checkSonsLen(it, 1, c.config) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 6653fd750342..f5d485091200 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -142,7 +142,7 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = - # This is needed for tgenericshardcases + # This is needed for tuninstantiatedgenericcalls # It's possible that a generic param will be used in a proc call to a # typedesc accepting proc. After generic param substitution, such procs # should be optionally instantiated with the correct type. In order to @@ -250,7 +250,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = result = newNodeI(nkRecList, n.info) of nkStaticExpr: var n = prepareNode(cl, n) - #n = reResolveCallsWithTypedescParams(cl, n) + if uninstantiatedGenericCalls in cl.c.config.legacyFeatures: + n = reResolveCallsWithTypedescParams(cl, n) result = if cl.allowMetaTypes: n else: cl.c.semExpr(cl.c, n) if not cl.allowMetaTypes: diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 9016a0d3dc91..a3ce7ddf5fbe 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -150,7 +150,7 @@ pkg "sim" pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "ssostrings" -pkg "stew" +pkg "stew", "export NIMFLAGS=\"--legacy:uninstantiatedGenericCalls\"; nimble test" pkg "stint", "nim r stint.nim" pkg "strslice" pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim" diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim new file mode 100644 index 000000000000..668415c08869 --- /dev/null +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -0,0 +1,82 @@ +discard """ + matrix: "--legacy:uninstantiatedGenericCalls" +""" + +# Cases that used to work due to weird workarounds in the compiler +# that were removed due to unsound logic which breaks other code with statics. +# Ideally these work in the future (with the same behavior as the relevant +# parts being wrapped in an `untyped` call), but these are just compiled as +# regular expressions with unresolved generic parameter types which are +# special cased to behave as `untyped` in a couple places in the compiler, +# It's hard to do this without losing some information (by turning every call +# in `range` into `nkStaticExpr`) or compiler performance (by checking if +# the expression compiles then using `nkStaticExpr`, which does not always +# work since the compiler can wrongly treat unresolved generic params as +# real types). + +block: + type Base10 = object + + func maxLen(T: typedesc[Base10], I: type): int8 = + when I is uint8: + 3 + elif I is uint16: + 5 + elif I is uint32: + 10 + elif I is uint64: + 20 + else: + when sizeof(uint) == 4: + 10 + else: + 20 + + type + Base10Buf[T: SomeUnsignedInt] = object + data: array[maxLen(Base10, T), byte] + # workaround without legacy switch is `untyped maxLen(Base10, T)` + len: int8 + + var x: Base10Buf[uint32] + doAssert x.data.len == 10 + var y: Base10Buf[uint16] + doAssert y.data.len == 5 + +import typetraits + +block: + proc typeNameLen(x: typedesc): int = + result = x.name.len + macro selectType(a, b: typedesc): typedesc = + result = a + + type + Foo[T] = object + data1: array[T.high, int] + data2: array[typeNameLen(T), float] + # workaround without legacy switch is `untyped typeNameLen(T)` + data3: array[0..T.typeNameLen, selectType(float, int)] + # workaround without legacy switch is `untyped T.typeNameLen`` + MyEnum = enum A, B, C, D + + var f1: Foo[MyEnum] + var f2: Foo[int8] + + doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2 + doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5 + + doAssert high(f2.data1) == 126 # 127 - 1 == 126 + doAssert high(f2.data2) == 3 # int8.len - 1 == 3 + + static: + doAssert high(f1.data1) == ord(C) + doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high + + doAssert high(f2.data1) == 126 + doAssert high(f2.data2) == 3 + + doAssert high(f1.data3) == 6 # length of MyEnum + doAssert high(f2.data3) == 4 # length of int8 + + doAssert f2.data3[0] is float diff --git a/tests/statictypes/tstaticgenericparam.nim b/tests/statictypes/tstaticgenericparam.nim index 4b2033642260..1786ef2c80cd 100644 --- a/tests/statictypes/tstaticgenericparam.nim +++ b/tests/statictypes/tstaticgenericparam.nim @@ -49,6 +49,7 @@ block: doAssert x is array[a .. c, int] block: + # `untyped` needed here for now because compiler expects `T` to support `+`: type Foo[T; U: static T] = array[T(0) .. untyped U + 1, int] block: From 0fbd7637d67bbcfee7700e5418173c7652213160 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 10 Jun 2023 22:00:32 +0300 Subject: [PATCH 03/11] convert to experimental --- compiler/options.nim | 2 +- compiler/seminst.nim | 4 +- compiler/semtypinst.nim | 2 +- testament/important_packages.nim | 2 +- tests/generics/tgenerics_various.nim | 36 ------- .../generics/tuninstantiatedgenericcalls.nim | 65 +++++++---- tests/statictypes/tgenericcomputedrange.nim | 101 ++++++++++++++++++ tests/statictypes/tgenericobjectarray.nim | 47 -------- tests/statictypes/tstaticgenericparam.nim | 50 --------- 9 files changed, 148 insertions(+), 161 deletions(-) create mode 100644 tests/statictypes/tgenericcomputedrange.nim delete mode 100644 tests/statictypes/tgenericobjectarray.nim diff --git a/compiler/options.nim b/compiler/options.nim index 1cefcb6bd082..8b710690ab11 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -218,6 +218,7 @@ type strictEffects, unicodeOperators, # deadcode flexibleOptionalParams, + genericBodyInstantiateCalls, strictDefs, strictCaseObjects @@ -233,7 +234,6 @@ type laxEffects ## Lax effects system prior to Nim 2.0. verboseTypeMismatch - uninstantiatedGenericCalls SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f520afce1224..c74fa344a379 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,8 +388,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - if not (uninstantiatedGenericCalls in c.config.legacyFeatures and - c.inGenericContext != 0): + if c.inGenericContext == 0 or + genericBodyInstantiateCalls in c.config.features: instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f5d485091200..d68393e3ff47 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -250,7 +250,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = result = newNodeI(nkRecList, n.info) of nkStaticExpr: var n = prepareNode(cl, n) - if uninstantiatedGenericCalls in cl.c.config.legacyFeatures: + if genericBodyInstantiateCalls notin cl.c.config.features: n = reResolveCallsWithTypedescParams(cl, n) result = if cl.allowMetaTypes: n else: cl.c.semExpr(cl.c, n) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a3ce7ddf5fbe..9016a0d3dc91 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -150,7 +150,7 @@ pkg "sim" pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "ssostrings" -pkg "stew", "export NIMFLAGS=\"--legacy:uninstantiatedGenericCalls\"; nimble test" +pkg "stew" pkg "stint", "nim r stint.nim" pkg "strslice" pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim" diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index 14634e7bf961..aff851981d61 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -127,42 +127,6 @@ block trefs: -block thardcases: - proc typeNameLen(x: typedesc): int {.compileTime.} = - result = x.name.len - macro selectType(a, b: typedesc): typedesc = - result = a - - type - Foo[T] = object - data1: array[T.high, int] - data2: array[untyped typeNameLen(T), float] - data3: array[0..untyped T.typeNameLen, selectType(float, int)] - MyEnum = enum A, B, C, D - - var f1: Foo[MyEnum] - var f2: Foo[int8] - - doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2 - doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5 - - doAssert high(f2.data1) == 126 # 127 - 1 == 126 - doAssert high(f2.data2) == 3 # int8.len - 1 == 3 - - static: - doAssert high(f1.data1) == ord(C) - doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high - - doAssert high(f2.data1) == 126 - doAssert high(f2.data2) == 3 - - doAssert high(f1.data3) == 6 # length of MyEnum - doAssert high(f2.data3) == 4 # length of int8 - - doAssert f2.data3[0] is float - - - block tmap_auto: let x = map(@[1, 2, 3], x => x+10) doAssert x == @[11, 12, 13] diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 668415c08869..e6e425d779a3 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -1,14 +1,16 @@ discard """ - matrix: "--legacy:uninstantiatedGenericCalls" + matrix: "; -d:useUntyped; --experimental:genericBodyInstantiateCalls -d:useUntyped" """ -# Cases that used to work due to weird workarounds in the compiler -# that were removed due to unsound logic which breaks other code with statics. -# Ideally these work in the future (with the same behavior as the relevant -# parts being wrapped in an `untyped` call), but these are just compiled as -# regular expressions with unresolved generic parameter types which are -# special cased to behave as `untyped` in a couple places in the compiler, -# It's hard to do this without losing some information (by turning every call +# Cases that work due to weird workarounds in the compiler involving not +# instantiating calls in generic bodies which are removed with +# --experimental:instantiatedGenericCalls due to breaking statics. +# Ideally these work in the future, with the same behavior as the relevant +# parts being wrapped in an `untyped` call. The issue is that these calls are +# compiled as regular expressions at the generic declaration with +# unresolved generic parameter types, which are special cased in some +# places in the compiler, but sometimes treated like real types. +# It's hard to fix this without losing some information (by turning every call # in `range` into `nkStaticExpr`) or compiler performance (by checking if # the expression compiles then using `nkStaticExpr`, which does not always # work since the compiler can wrongly treat unresolved generic params as @@ -31,12 +33,19 @@ block: 10 else: 20 - - type - Base10Buf[T: SomeUnsignedInt] = object - data: array[maxLen(Base10, T), byte] - # workaround without legacy switch is `untyped maxLen(Base10, T)` - len: int8 + + when not defined(useUntyped): + type + Base10Buf[T: SomeUnsignedInt] = object + data: array[maxLen(Base10, T), byte] + # workaround for experimental switch is `untyped maxLen(Base10, T)` + len: int8 + else: + type + Base10Buf[T: SomeUnsignedInt] = object + data: array[untyped maxLen(Base10, T), byte] + # test workaround + len: int8 var x: Base10Buf[uint32] doAssert x.data.len == 10 @@ -45,20 +54,30 @@ block: import typetraits -block: +block thardcases: proc typeNameLen(x: typedesc): int = result = x.name.len macro selectType(a, b: typedesc): typedesc = result = a - type - Foo[T] = object - data1: array[T.high, int] - data2: array[typeNameLen(T), float] - # workaround without legacy switch is `untyped typeNameLen(T)` - data3: array[0..T.typeNameLen, selectType(float, int)] - # workaround without legacy switch is `untyped T.typeNameLen`` - MyEnum = enum A, B, C, D + when not defined(useUntyped): + type + Foo[T] = object + data1: array[T.high, int] + data2: array[typeNameLen(T), float] + # workaround for experimental switch is `untyped typeNameLen(T)` + data3: array[0..T.typeNameLen, selectType(float, int)] + # workaround for experimental switch is `untyped T.typeNameLen` + else: + type + Foo[T] = object + data1: array[T.high, int] + data2: array[untyped typeNameLen(T), float] + # test workaround + data3: array[0..untyped T.typeNameLen, selectType(float, int)] + # test workaround + + type MyEnum = enum A, B, C, D var f1: Foo[MyEnum] var f2: Foo[int8] diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim new file mode 100644 index 000000000000..6576399ea0bd --- /dev/null +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -0,0 +1,101 @@ +discard """ + matrix: "--experimental:genericBodyInstantiateCalls" +""" + +import math + +block: # issue #19916 + type + Test[S: static[Natural]] = object + a: array[ceilDiv(S, 8), uint8] + + let a = Test[32]() + doAssert a.a.len == 4 + +block: # issue #20514 + type Foo[S:static[array[2, int]]] = object + values: array[prod(S), float] + + doAssert Foo[[4,5]]().values.len == 20 + +block: # issue #20937 + type + Vec3[T: SomeNumber] {.bycopy.} = tuple[x, y, z: T] + + func volume[T](v: Vec3[T]): T = + when T is SomeUnsignedInt: + v.x * v.y * v.z + else: + abs (v.x * v.y * v.z) + + type + Matrix3[C: static Vec3[uint], T] = object + cells: array[C.volume, T] + + let m = Matrix3[(1.uint, 1.uint, 1.uint), uint](cells: [0.uint]) + let m2 = Matrix3[(4.uint, 3.uint, 5.uint), uint]() + doAssert m2.cells.len == 60 + +block: # issue #19284 + type Board[N, M: static Slice[int]] = array[len(N)*len(M), int8] + + var t: Board[0..4, 0..4] + doAssert t.len == 25 + +block: # minimal issue #19284 + proc foo[T](x: T): int = + result = 0 + type Foo[N: static int] = array[0..foo(N), int] + + var t: Foo[5] + doAssert t.len == 1 + +block: + type Foo[T; U: static T] = range[T(0) .. U] + + block: + var x: array[Foo[int, 1], int] + x[0] = 1 + x[1] = 2 + doAssert x == [0: 1, 1: 2] + doAssert x is array[0 .. 1, int] + + block: + type Bar = enum a, b, c + var x: array[Foo[Bar, c], int] + x[a] = 1 + x[b] = 2 + x[c] = 3 + doAssert x == [a: 1, b: 2, c: 3] + doAssert x is array[a .. c, int] + +block: + type Foo[T; U: static T] = array[T(0) .. U, int] + + block: + var x: Foo[int, 1] + x[0] = 1 + x[1] = 2 + doAssert x == [0: 1, 1: 2] + doAssert x is array[0 .. 1, int] + + block: + type Bar = enum a, b, c + var x: Foo[Bar, c] + x[a] = 1 + x[b] = 2 + x[c] = 3 + doAssert x == [a: 1, b: 2, c: 3] + doAssert x is array[a .. c, int] + +block: + # `untyped` needed here for now because compiler expects `T` to support `+`: + type Foo[T; U: static T] = array[T(0) .. untyped U + 1, int] + + block: + var x: Foo[int, 1] + x[0] = 1 + x[1] = 2 + x[2] = 3 + doAssert x == [0: 1, 1: 2, 2: 3] + doAssert x is array[0 .. 2, int] diff --git a/tests/statictypes/tgenericobjectarray.nim b/tests/statictypes/tgenericobjectarray.nim deleted file mode 100644 index e94b453b4751..000000000000 --- a/tests/statictypes/tgenericobjectarray.nim +++ /dev/null @@ -1,47 +0,0 @@ -import math - -block: # issue #19916 - type - Test[S: static[Natural]] = object - a: array[ceilDiv(S, 8), uint8] - - let a = Test[32]() - doAssert a.a.len == 4 - -block: # issue #20514 - type Foo[S:static[array[2, int]]] = object - values: array[prod(S), float] - - doAssert Foo[[4,5]]().values.len == 20 - -block: # issue #20937 - type - Vec3[T: SomeNumber] {.bycopy.} = tuple[x, y, z: T] - - func volume[T](v: Vec3[T]): T = - when T is SomeUnsignedInt: - v.x * v.y * v.z - else: - abs (v.x * v.y * v.z) - - type - Matrix3[C: static Vec3[uint], T] = object - cells: array[C.volume, T] - - let m = Matrix3[(1.uint, 1.uint, 1.uint), uint](cells: [0.uint]) - let m2 = Matrix3[(4.uint, 3.uint, 5.uint), uint]() - doAssert m2.cells.len == 60 - -block: # issue #19284 - type Board[N, M: static Slice[int]] = array[len(N)*len(M), int8] - - var t: Board[0..4, 0..4] - doAssert t.len == 25 - -block: # minimal issue #19284 - proc foo[T](x: T): int = - result = 0 - type Foo[N: static int] = array[0..foo(N), int] - - var t: Foo[5] - doAssert t.len == 1 diff --git a/tests/statictypes/tstaticgenericparam.nim b/tests/statictypes/tstaticgenericparam.nim index 1786ef2c80cd..3c77882d1ab9 100644 --- a/tests/statictypes/tstaticgenericparam.nim +++ b/tests/statictypes/tstaticgenericparam.nim @@ -9,53 +9,3 @@ block: # issue #19365 doAssert ss == @["123: int"] f("abc") doAssert ss == @["123: int", "abc: string"] - -block: - type Foo[T; U: static T] = range[T(0) .. U] - - block: - var x: array[Foo[int, 1], int] - x[0] = 1 - x[1] = 2 - doAssert x == [0: 1, 1: 2] - doAssert x is array[0 .. 1, int] - - block: - type Bar = enum a, b, c - var x: array[Foo[Bar, c], int] - x[a] = 1 - x[b] = 2 - x[c] = 3 - doAssert x == [a: 1, b: 2, c: 3] - doAssert x is array[a .. c, int] - -block: - type Foo[T; U: static T] = array[T(0) .. U, int] - - block: - var x: Foo[int, 1] - x[0] = 1 - x[1] = 2 - doAssert x == [0: 1, 1: 2] - doAssert x is array[0 .. 1, int] - - block: - type Bar = enum a, b, c - var x: Foo[Bar, c] - x[a] = 1 - x[b] = 2 - x[c] = 3 - doAssert x == [a: 1, b: 2, c: 3] - doAssert x is array[a .. c, int] - -block: - # `untyped` needed here for now because compiler expects `T` to support `+`: - type Foo[T; U: static T] = array[T(0) .. untyped U + 1, int] - - block: - var x: Foo[int, 1] - x[0] = 1 - x[1] = 2 - x[2] = 3 - doAssert x == [0: 1, 1: 2, 2: 3] - doAssert x is array[0 .. 2, int] From 79f00820909691e4464142db4d789de5d94f9ac7 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 10 Jun 2023 22:41:13 +0300 Subject: [PATCH 04/11] apparently untyped needs the experimental switch --- tests/generics/tuninstantiatedgenericcalls.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index e6e425d779a3..cd07b447a37c 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -1,5 +1,5 @@ discard """ - matrix: "; -d:useUntyped; --experimental:genericBodyInstantiateCalls -d:useUntyped" + matrix: "; --experimental:genericBodyInstantiateCalls -d:useUntyped" """ # Cases that work due to weird workarounds in the compiler involving not @@ -55,7 +55,7 @@ block: import typetraits block thardcases: - proc typeNameLen(x: typedesc): int = + proc typeNameLen(x: typedesc): int {.compileTime.} = result = x.name.len macro selectType(a, b: typedesc): typedesc = result = a From 8353d0594ef037a1e4880fea6de619d02f61b38b Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Mon, 12 Jun 2023 22:57:30 +0300 Subject: [PATCH 05/11] try special case call semcheck --- compiler/semcall.nim | 5 ++++- compiler/semexprs.nim | 5 ++++- compiler/sigmatch.nim | 4 ++-- tests/generics/tuninstantiatedgenericcalls.nim | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8445b25e71b8..4f1575a2581c 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -627,7 +627,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags) else: - if efExplain notin flags: + if c.inGenericContext > 0: + result = n + result.typ = makeTypeFromExpr(c, result.copyTree) + elif efExplain notin flags: # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cf9553c38922..6a73aa8fd7d1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -978,7 +978,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - internalError(c.config, "semOverloadedCallAnalyseEffects") + if c.inGenericContext == 0: + internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym case callee.kind @@ -1014,6 +1015,8 @@ proc setGenericParams(c: PContext, n: PNode) = proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError: return errorNode(c, n) + if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0: + return n result = n diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c3d5247919fe..46ec9c05895f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1837,8 +1837,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier - if a.kind != tyTypeDesc: - if a.kind == tyGenericParam and tfWildcard in a.flags: + if a.kind != tyTypeDesc or a.base.kind == tyGenericParam: + if false and a.kind == tyGenericParam and tfWildcard in a.flags: # TODO: prevent `a` from matching as a wildcard again result = isGeneric else: diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index cd07b447a37c..b4477d9a88cc 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -1,5 +1,5 @@ discard """ - matrix: "; --experimental:genericBodyInstantiateCalls -d:useUntyped" + matrix: "; --experimental:genericBodyInstantiateCalls; --experimental:genericBodyInstantiateCalls -d:useUntyped" """ # Cases that work due to weird workarounds in the compiler involving not From 5b545e1b46cd9c54faaf367f3f35189271edafbc Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:30:16 +0300 Subject: [PATCH 06/11] try fix --- compiler/semcall.nim | 2 +- compiler/seminst.nim | 2 +- compiler/semtypinst.nim | 2 +- compiler/sigmatch.nim | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 4f1575a2581c..c0317f4d68e3 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -627,7 +627,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags) else: - if c.inGenericContext > 0: + if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: result = n result.typ = makeTypeFromExpr(c, result.copyTree) elif efExplain notin flags: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index c74fa344a379..069667fd995e 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,7 +388,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - if c.inGenericContext == 0 or + if c.inGenericContext == 0 or true or genericBodyInstantiateCalls in c.config.features: instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d68393e3ff47..7f7fa620b89a 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -250,7 +250,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = result = newNodeI(nkRecList, n.info) of nkStaticExpr: var n = prepareNode(cl, n) - if genericBodyInstantiateCalls notin cl.c.config.features: + if false and genericBodyInstantiateCalls notin cl.c.config.features: n = reResolveCallsWithTypedescParams(cl, n) result = if cl.allowMetaTypes: n else: cl.c.semExpr(cl.c, n) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 46ec9c05895f..8403b8da8071 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1838,7 +1838,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier if a.kind != tyTypeDesc or a.base.kind == tyGenericParam: - if false and a.kind == tyGenericParam and tfWildcard in a.flags: + if (c.c.inGenericContext == 0 or c.matchedConcept != nil) and + a.kind == tyGenericParam and tfWildcard in a.flags: # TODO: prevent `a` from matching as a wildcard again result = isGeneric else: From 3c32cb7aa14518e4d1d88e29dd5cf710b771c071 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:32:16 +0300 Subject: [PATCH 07/11] fix compilation --- compiler/sigmatch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 8403b8da8071..319877732fb1 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1838,7 +1838,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier if a.kind != tyTypeDesc or a.base.kind == tyGenericParam: - if (c.c.inGenericContext == 0 or c.matchedConcept != nil) and + if (c.c.inGenericContext == 0 or c.c.matchedConcept != nil) and a.kind == tyGenericParam and tfWildcard in a.flags: # TODO: prevent `a` from matching as a wildcard again result = isGeneric From c697132d505895129f9ce6ae9406fed2d72529d0 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:40:46 +0300 Subject: [PATCH 08/11] final cleanup, not experimental, make `when` work --- compiler/options.nim | 1 - compiler/semexprs.nim | 3 +- compiler/seminst.nim | 4 +- compiler/semtypes.nim | 6 +- compiler/semtypinst.nim | 45 ++++++------- compiler/sigmatch.nim | 11 +++- .../generics/tuninstantiatedgenericcalls.nim | 65 +++++++------------ tests/statictypes/tgenericcomputedrange.nim | 4 -- 8 files changed, 63 insertions(+), 76 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 8b710690ab11..7e9e37384fbe 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -218,7 +218,6 @@ type strictEffects, unicodeOperators, # deadcode flexibleOptionalParams, - genericBodyInstantiateCalls, strictDefs, strictCaseObjects diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6a73aa8fd7d1..259be8461fc4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -340,6 +340,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = targetType = targetType.base of tyUntyped: if c.inGenericContext > 0: + # could add `and efDetermineType in flags`, but no flags param result = n[1] result.typ = makeTypeFromExpr(c, result.copyTree) return @@ -978,7 +979,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - if c.inGenericContext == 0: + if not (efDetermineType in flags and c.inGenericContext > 0): internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 069667fd995e..b4b5ad0a2371 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,9 +388,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - if c.inGenericContext == 0 or true or - genericBodyInstantiateCalls in c.config.features: - instantiateBody(c, n, fn.typ.n, result, fn) + instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 39957546cae5..a9fc980dbfa7 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -744,7 +744,11 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, else: # XXX this is still a hard compilation in a generic context, this can # result in unresolved generic parameters being treated like real types - it[0] = forceBool(c, semExprWithType(c, it[0])) + let e = semExprWithType(c, it[0], {efDetermineType}) + if e.typ.kind == tyFromExpr: + it[0] = makeStaticExpr(c, e) + else: + it[0] = forceBool(c, e) of nkElse: checkSonsLen(it, 1, c.config) if branch == nil: branch = it[0] diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 7f7fa620b89a..7c9bf9039d1e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -141,27 +141,28 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skGenericParam or (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) -proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = - # This is needed for tuninstantiatedgenericcalls - # It's possible that a generic param will be used in a proc call to a - # typedesc accepting proc. After generic param substitution, such procs - # should be optionally instantiated with the correct type. In order to - # perform this instantiation, we need to re-run the generateInstance path - # in the compiler, but it's quite complicated to do so at the moment so we - # resort to a mild hack; the head symbol of the call is temporary reset and - # overload resolution is executed again (which may trigger generateInstance). - if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags: - var needsFixing = false - for i in 1.. 0 and + a.skipTypes({tyTypeDesc}).kind == tyGenericParam: + # generic type bodies can sometimes compile call expressions + # prevent unresolved generic parameters from being passed to procs as + # typedesc parameters + result = isNone + elif a.kind != tyTypeDesc: + if a.kind == tyGenericParam and tfWildcard in a.flags: # TODO: prevent `a` from matching as a wildcard again result = isGeneric else: diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index b4477d9a88cc..2a7914d2376b 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -1,20 +1,10 @@ -discard """ - matrix: "; --experimental:genericBodyInstantiateCalls; --experimental:genericBodyInstantiateCalls -d:useUntyped" -""" - -# Cases that work due to weird workarounds in the compiler involving not -# instantiating calls in generic bodies which are removed with -# --experimental:instantiatedGenericCalls due to breaking statics. -# Ideally these work in the future, with the same behavior as the relevant -# parts being wrapped in an `untyped` call. The issue is that these calls are -# compiled as regular expressions at the generic declaration with -# unresolved generic parameter types, which are special cased in some -# places in the compiler, but sometimes treated like real types. -# It's hard to fix this without losing some information (by turning every call -# in `range` into `nkStaticExpr`) or compiler performance (by checking if -# the expression compiles then using `nkStaticExpr`, which does not always -# work since the compiler can wrongly treat unresolved generic params as -# real types). +# Cases that used to only work due to weird workarounds in the compiler +# involving not instantiating calls in generic bodies which are removed +# due to breaking statics. +# The issue was that these calls are compiled as regular expressions at +# the generic declaration with unresolved generic parameter types, +# which are special cased in some places in the compiler, but sometimes +# treated like real types. block: type Base10 = object @@ -34,23 +24,27 @@ block: else: 20 - when not defined(useUntyped): + block: type Base10Buf[T: SomeUnsignedInt] = object data: array[maxLen(Base10, T), byte] - # workaround for experimental switch is `untyped maxLen(Base10, T)` len: int8 - else: + + var x: Base10Buf[uint32] + doAssert x.data.len == 10 + var y: Base10Buf[uint16] + doAssert y.data.len == 5 + block: # with `untyped` type Base10Buf[T: SomeUnsignedInt] = object data: array[untyped maxLen(Base10, T), byte] - # test workaround + # same as what the compiler infers above len: int8 - var x: Base10Buf[uint32] - doAssert x.data.len == 10 - var y: Base10Buf[uint16] - doAssert y.data.len == 5 + var x: Base10Buf[uint32] + doAssert x.data.len == 10 + var y: Base10Buf[uint16] + doAssert y.data.len == 5 import typetraits @@ -60,22 +54,11 @@ block thardcases: macro selectType(a, b: typedesc): typedesc = result = a - when not defined(useUntyped): - type - Foo[T] = object - data1: array[T.high, int] - data2: array[typeNameLen(T), float] - # workaround for experimental switch is `untyped typeNameLen(T)` - data3: array[0..T.typeNameLen, selectType(float, int)] - # workaround for experimental switch is `untyped T.typeNameLen` - else: - type - Foo[T] = object - data1: array[T.high, int] - data2: array[untyped typeNameLen(T), float] - # test workaround - data3: array[0..untyped T.typeNameLen, selectType(float, int)] - # test workaround + type + Foo[T] = object + data1: array[T.high, int] + data2: array[typeNameLen(T), float] + data3: array[0..T.typeNameLen, selectType(float, int)] type MyEnum = enum A, B, C, D diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim index 6576399ea0bd..15e5f2d16fed 100644 --- a/tests/statictypes/tgenericcomputedrange.nim +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -1,7 +1,3 @@ -discard """ - matrix: "--experimental:genericBodyInstantiateCalls" -""" - import math block: # issue #19916 From 6a5a41fd80df160711200fb9a53c5fab71d0ede0 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:42:56 +0300 Subject: [PATCH 09/11] remove last needed use of untyped --- tests/statictypes/tgenericcomputedrange.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim index 15e5f2d16fed..dcd9b48a8e82 100644 --- a/tests/statictypes/tgenericcomputedrange.nim +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -85,8 +85,7 @@ block: doAssert x is array[a .. c, int] block: - # `untyped` needed here for now because compiler expects `T` to support `+`: - type Foo[T; U: static T] = array[T(0) .. untyped U + 1, int] + type Foo[T; U: static T] = array[T(0) .. U + 1, int] block: var x: Foo[int, 1] From f7fccfa3652cfc7bb8583d9303301c18ad1064ff Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:43:32 +0300 Subject: [PATCH 10/11] fix unused warning in test --- tests/statictypes/tgenericcomputedrange.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim index dcd9b48a8e82..01ec4767a671 100644 --- a/tests/statictypes/tgenericcomputedrange.nim +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -29,6 +29,7 @@ block: # issue #20937 cells: array[C.volume, T] let m = Matrix3[(1.uint, 1.uint, 1.uint), uint](cells: [0.uint]) + doAssert m.cells.len == 1 let m2 = Matrix3[(4.uint, 3.uint, 5.uint), uint]() doAssert m2.cells.len == 60 From 790bf15598332ef43924110aefb84980b5b2fe4a Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:41:10 +0300 Subject: [PATCH 11/11] remove untyped feature --- compiler/semexprs.nim | 10 +------ .../generics/tuninstantiatedgenericcalls.nim | 30 ++++++------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 259be8461fc4..3b40425cf47f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -338,15 +338,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = return evaluated else: targetType = targetType.base - of tyUntyped: - if c.inGenericContext > 0: - # could add `and efDetermineType in flags`, but no flags param - result = n[1] - result.typ = makeTypeFromExpr(c, result.copyTree) - return - else: - localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) - of tyAnything, tyTyped: + of tyAnything, tyUntyped, tyTyped: localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) else: discard diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 2a7914d2376b..c4d95a0fb021 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -24,27 +24,15 @@ block: else: 20 - block: - type - Base10Buf[T: SomeUnsignedInt] = object - data: array[maxLen(Base10, T), byte] - len: int8 - - var x: Base10Buf[uint32] - doAssert x.data.len == 10 - var y: Base10Buf[uint16] - doAssert y.data.len == 5 - block: # with `untyped` - type - Base10Buf[T: SomeUnsignedInt] = object - data: array[untyped maxLen(Base10, T), byte] - # same as what the compiler infers above - len: int8 - - var x: Base10Buf[uint32] - doAssert x.data.len == 10 - var y: Base10Buf[uint16] - doAssert y.data.len == 5 + type + Base10Buf[T: SomeUnsignedInt] = object + data: array[maxLen(Base10, T), byte] + len: int8 + + var x: Base10Buf[uint32] + doAssert x.data.len == 10 + var y: Base10Buf[uint16] + doAssert y.data.len == 5 import typetraits