diff --git a/compiler/ast.nim b/compiler/ast.nim index 8286e3bb72f6..a28a7e7e3381 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -981,6 +981,7 @@ const nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit} + nkFloatLiterals* = {nkFloatLit..nkFloat128Lit} nkLambdaKinds* = {nkLambda, nkDo} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs* = nkLambdaKinds + declarativeDefs @@ -1476,7 +1477,7 @@ proc copyNode*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1495,7 +1496,7 @@ proc shallowCopy*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1515,7 +1516,7 @@ proc copyTree*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1564,7 +1565,7 @@ proc getInt*(a: PNode): BiggestInt = proc getFloat*(a: PNode): BiggestFloat = case a.kind - of nkFloatLit..nkFloat128Lit: result = a.floatVal + of nkFloatLiterals: result = a.floatVal else: internalError(a.info, "getFloat") result = 0.0 diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bdba34e36108..8068cc572364 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -841,6 +841,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # always call for sideeffects: assert t.kind != tyTuple discard getRecordDesc(m, t, result, check) + # The resulting type will include commas and these won't play well + # with the C macros for defining procs such as N_NIMCALL. We must + # create a typedef for the type and use it in the proc signature: + let typedefName = ~"TY" & $sig + addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName]) + m.typeCache[sig] = typedefName + result = typedefName else: when false: if t.sym != nil and t.sym.name.s == "KeyValuePair": diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c409acc5993d..0675c5ca007a 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -44,6 +44,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = case x.kind of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) + of nkLiterals - nkFloatLiterals: id.add(x.renderTree) else: handleError(n, origin) result = getIdent(id) of nkOpenSymChoice, nkClosedSymChoice: @@ -456,4 +457,4 @@ proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; a = nextOverloadIter(o, c, n) proc isInfixAs*(n: PNode): bool = - return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" \ No newline at end of file + return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8e3aeffbe6ce..500d343add2c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -880,6 +880,9 @@ const proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = + # Note: This function will return emptyNode when attempting to read + # a static type parameter that is not yet resolved (e.g. this may + # happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]` if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}: for statement in typ.n: case statement.kind @@ -910,7 +913,10 @@ proc readTypeParameter(c: PContext, typ: PType, if tParam.sym.name.id == paramName.id: let rawTyp = ty.sons[s + 1] if rawTyp.kind == tyStatic: - return rawTyp.n + if rawTyp.n != nil: + return rawTyp.n + else: + return emptyNode else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) @@ -1075,21 +1081,43 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = template tryReadingGenericParam(t: PType) = case t.kind of tyTypeParamsHolders: - return readTypeParameter(c, t, i, n.info) + result = readTypeParameter(c, t, i, n.info) + if result == emptyNode: + result = n + n.typ = makeTypeFromExpr(c, n.copyTree) + return of tyUserTypeClasses: if t.isResolvedUserTypeClass: return readTypeParameter(c, t, i, n.info) else: n.typ = makeTypeFromExpr(c, copyTree(n)) return n - of tyGenericParam: + of tyGenericParam, tyAnything: n.typ = makeTypeFromExpr(c, copyTree(n)) return n else: discard - if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): - if ty.kind == tyTypeDesc: ty = ty.base + var argIsType = false + + if ty.kind == tyTypeDesc: + if ty.base.kind == tyNone: + # This is a still unresolved typedesc parameter. + # If this is a regular proc, then all bets are off and we must return + # tyFromExpr, but when this happen in a macro this is not a built-in + # field access and we leave the compiler to compile a normal call: + if getCurrOwner(c).kind != skMacro: + n.typ = makeTypeFromExpr(c, n.copyTree) + return n + else: + return nil + else: + ty = ty.base + argIsType = true + else: + argIsType = isTypeExpr(n.sons[0]) + + if argIsType: ty = ty.skipTypes(tyDotOpTransparent) case ty.kind of tyEnum: @@ -2172,7 +2200,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! result = semSym(c, n, n.sym, flags) - of nkEmpty, nkNone, nkCommentStmt: + of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: if result.typ == nil: result.typ = getSysType(tyNil) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index acea9330b820..7fedaca5bd13 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -174,6 +174,8 @@ proc sideEffectsCheck(c: PContext, s: PSym) = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = + internalAssert header.kind == tyGenericInvocation + var typeMap: LayeredIdTable cl: TReplTypeVars @@ -185,7 +187,35 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes + + # We must add all generic params in scope, because the generic body + # may include tyFromExpr nodes depending on these generic params. + # XXX: This looks quite similar to the code in matchUserTypeClass, + # perhaps the code can be extracted in a shared function. + openScope(c) + let genericTyp = header.base + for i in 0 .. (genericTyp.len - 2): + let genParam = genericTyp[i] + var param: PSym + + template paramSym(kind): untyped = + newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info) + + if genParam.kind == tyStatic: + param = paramSym skConst + param.ast = header[i+1].n + param.typ = header[i+1] + else: + param = paramSym skType + param.typ = makeTypeDesc(c, header[i+1]) + + # this scope was not created by the user, + # unused params shoudn't be reported. + param.flags.incl sfUsed + addDecl(c, param) + result = replaceTypeVarsT(cl, header) + closeScope(c) proc instantiateProcType(c: PContext, pt: TIdTable, prc: PSym, info: TLineInfo) = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d097a373ffc8..544edef26c55 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1396,7 +1396,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = fixupTypeOf(c, prev, typExpr) result = typExpr.typ else: - result = semTypeExpr(c, n, prev) + if c.inGenericContext > 0 and n.kind == nkCall: + result = makeTypeFromExpr(c, n.copyTree) + else: + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d9b368b0e7fa..09434c925b82 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -460,6 +460,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = of tyFromExpr: if cl.allowMetaTypes: return + # This assert is triggered when a tyFromExpr was created in a cyclic + # way. You should break the cycle at the point of creation by introducing + # a call such as: `n.typ = makeTypeFromExpr(c, n.copyTree)` + # Otherwise, the cycle will be fatal for the prepareNode call below assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e4509ff913a5..b6a1a007c090 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1883,7 +1883,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, return arg elif f.kind == tyTypeDesc: return arg - elif f.kind == tyStatic: + elif f.kind == tyStatic and arg.typ.n != nil: return arg.typ.n else: return argSemantized # argOrig diff --git a/compiler/types.nim b/compiler/types.nim index 7ae02486eeb5..ff3572517580 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1136,6 +1136,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyTypeClasses: if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: discard + elif t.isResolvedUserTypeClass: + result = typeAllowedAux(marker, t.lastSon, kind, flags) elif kind notin {skParam, skResult}: result = t of tyGenericBody, tyGenericParam, tyGenericInvocation, diff --git a/tests/concepts/tseqofconcept.nim b/tests/concepts/tseqofconcept.nim new file mode 100644 index 000000000000..5e44117ea3eb --- /dev/null +++ b/tests/concepts/tseqofconcept.nim @@ -0,0 +1,19 @@ +discard """ +output: "1\n2\n3" +""" + +type + MyConcept = concept x + someProc(x) + + SomeSeq = seq[MyConcept] + +proc someProc(x:int) = echo x + +proc work (s: SomeSeq) = + for item in s: + someProc item + +var s = @[1, 2, 3] +work s + diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index 9365a3a18879..f81d67a5057a 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -300,7 +300,7 @@ reject wantsVarPointer2(pcat) # covariance may be allowed for certain extern types -{.emit: """ +{.emit: """/*TYPESECTION*/ template struct FN { typedef void (*type)(T); }; template struct ARR { typedef T DataType[2]; DataType data; }; """.} diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim index 9df3754ba371..4d686955fef8 100644 --- a/tests/cpp/tvector_iterator.nim +++ b/tests/cpp/tvector_iterator.nim @@ -2,7 +2,7 @@ discard """ targets: "cpp" """ -{.emit: """ +{.emit: """/*TYPESECTION*/ template struct Vector { diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index bd32958743c4..02021185f83c 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -14,7 +14,6 @@ AST b 20Test 20 ''' - disabled: true """ import macros @@ -26,7 +25,7 @@ type const data: Tconfig = (@["aa", "bb"], @[11, 22]) -macro mymacro(data: static[TConfig]) = +macro mymacro(data: static[TConfig]): untyped = echo "letters" for s in items(data.letters): echo s @@ -44,10 +43,10 @@ const a : Ta = @[(11, 22), (33, 44)] b : Tb = (@[55,66], @[77, 88]) -macro mA(data: static[Ta]) = +macro mA(data: static[Ta]): untyped = echo "AST a \n", repr(data) -macro mB(data: static[Tb]) = +macro mB(data: static[Tb]): untyped = echo "AST b \n", repr(data) echo data.e[0] @@ -57,13 +56,15 @@ mB(b) type Foo[N: static[int], Z: static[string]] = object -macro staticIntMacro(f: static[int]) = echo f +macro staticIntMacro(f: static[int]): untyped = + echo f + staticIntMacro 10 var x: Foo[20, "Test"] -macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12) = +macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): untyped = echo N, Z genericMacro x diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index c29fd15ce91c..1209fe78f2e6 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -1,4 +1,9 @@ -import macros +discard """ +output: "8\n8\n4" +""" + +import + macros, typetraits template selectType(x: int): typeDesc = when x < 10: @@ -11,6 +16,9 @@ template simpleTypeTempl: typeDesc = macro typeFromMacro: typedesc = string +# The tests below check that the result variable of the +# selected type matches the literal types in the code: + proc t1*(x: int): simpleTypeTempl() = result = "test" @@ -37,3 +45,57 @@ proc t6*(x: type(t3(0))): type(t1(0)) = proc t7*(x: int): type($x) = result = "test" +# This is a more compicated example involving a type +# selection through a macro: +# https://github.com/nim-lang/Nim/issues/7230 + +macro getBase*(bits: static[int]): untyped = + if bits >= 128: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) + else: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) + +type + BaseUint* = SomeUnsignedInt or MpUintBase + + MpUintBase*[T] = object + lo*, hi*: T + + ## This gets type mismatch + MpUint*[bits: static[int]] = getBase(bits) + +var m1: MpUint[128] +var m2: MpUint[64] +var m3: getBase(32) + +static: + # assert m1.type.name == "MpUintBase[uint64]" + assert m1.lo.type.name == "uint64" + assert m2.lo.type.name == "uint32" + assert m3.type.name == "MpUintBase[system.uint32]" + +# https://github.com/nim-lang/Nim/issues/7379 + +import macros, typetraits + +macro works(): untyped = + result = getType(int64) + +macro fails(bits: static[int]): untyped = + if bits > 64: + result = getType(int64) + else: + result = getType(int32) + +type + Foo*[bits: static[int]] = works() + Bar*[bits: static[int]] = fails(bits) + +var a: Foo[16] +var b: Bar[256] +var c: Bar[32] + +echo sizeof(a) +echo sizeof(b) +echo sizeof(c) + diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim index 8b99eb501d80..c845e04f7995 100644 --- a/tests/metatype/typeclassinference.nim +++ b/tests/metatype/typeclassinference.nim @@ -1,7 +1,6 @@ discard """ errormsg: "type mismatch: got but expected 'ptr'" line: 20 - disabled: true """ import typetraits @@ -12,7 +11,7 @@ type var x = Vec([1, 2, 3]) static: - assert x.type.name == "Vec[static[int](3), int]" + assert x.type.name == "Vec[3, system.int]" var str1: string = "hello, world!" var ptr1: ptr = addr(str1) diff --git a/tests/misc/tidentconcatenations.nim b/tests/misc/tidentconcatenations.nim new file mode 100644 index 000000000000..302c51d87cd0 --- /dev/null +++ b/tests/misc/tidentconcatenations.nim @@ -0,0 +1,32 @@ +type + Hash*[bits: static[int]] = object + data*: array[bits div 8, uint8] + +{.emit: """ + +void sha_256(void* input, int input_len, void* output, int output_len) {} +void sha_512(void* input, int input_len, void* output, int output_len) {} + +void keccak_256(void* input, int input_len, void* output, int output_len) {} +void keccak_512(void* input, int input_len, void* output, int output_len) {} + +""".} + +template defineKeccak(bits: untyped) = + proc `extKeccak bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "keccak_" & astToStr(bits).} + +template defineSha(bits: static[int]) = + proc `extSha bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "sha_" & astToStr(bits).} + +template defineHashProcs(bits) = + defineSha(bits) + defineKeccak(bits) + +defineHashProcs(256) +defineHashProcs(512) + +extSha256(nil, 0, nil, 0) +extSha512(nil, 0, nil, 0) +extKeccak256(nil, 0, nil, 0) +extKeccak512(nil, 0, nil, 0) + diff --git a/tests/statictypes/tcryptodigest.nim b/tests/statictypes/tcryptodigest.nim new file mode 100644 index 000000000000..c78a4188aca4 --- /dev/null +++ b/tests/statictypes/tcryptodigest.nim @@ -0,0 +1,44 @@ +discard """ + output: "Digest[128]\nDigest[256]" +""" + +import typetraits + +type + Digest[bits: static[int]] = object + data: array[bits div 8, byte] + + ContextKind = enum + A, B, C + + HashingContext[bits: static[int], kind: static[ContextKind]] = object + ctx: array[bits div 8, byte] + + Hash128 = HashingContext[128, A] + Hash256 = HashingContext[256, B] + + HMAC[HashType] = object + h: HashType + +proc init(c: var HashingContext) = discard +proc update(c: var HashingContext, data: ptr byte, dataLen: uint) = discard +proc finish(c: var HashingContext): Digest[c.bits] = discard + +proc digest(T: typedesc, data: ptr byte, dataLen: uint): Digest[T.bits] = + mixin init, update, finish + + var ctx: T + ctx.init() + ctx.update(data, dataLen) + result = ctx.finish() + +var h = Hash128.digest(nil, 0) +echo h.type.name + +proc finish(hmac: var HMAC): Digest[HMAC.HashType.bits] = + discard + +var hm: HMAC[Hash256] +var d = hm.finish +echo d.type.name + diff --git a/tests/statictypes/tstaticimportcpp.nim b/tests/statictypes/tstaticimportcpp.nim index e13cc36b453f..0cbbc1df65c4 100644 --- a/tests/statictypes/tstaticimportcpp.nim +++ b/tests/statictypes/tstaticimportcpp.nim @@ -1,9 +1,9 @@ discard """ targets: "cpp" -output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest" +output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest\n[0, 0, 20, 0]" """ -{.emit: """ +{.emit: """/*TYPESECTION*/ template struct GenericIntType { @@ -51,3 +51,9 @@ echo c.field echo d.field echo e.field +proc plus(a, b: GenInt4): GenInt4 = + for i in 0 ..< result.data.len: + result.data[i] = a.data[i] + b.data[i] + +echo plus(a, a).data +