From bc01835091df93d8d278a55c62338e96be646e46 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 17:07:54 +0300 Subject: [PATCH 01/19] Allow tyOr,tyAnd and tyNot to be constructed in more contexts --- compiler/semdata.nim | 4 ++++ compiler/semmagic.nim | 48 ++++++++++++++++++++++++++++--------------- lib/system.nim | 19 +++++++++++++++-- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8f2c802de..5da2b70fa 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -277,6 +277,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil result.n = n +proc newTypeWithSons2*(kind: TTypeKind, owner: PSym, sons: seq[PType]): PType = + result = newType(kind, owner) + result.sons = sons + proc newTypeWithSons*(c: PContext, kind: TTypeKind, sons: seq[PType]): PType = result = newType(kind, getCurrOwner(c)) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 3e1989eaf..eb6259df0 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -89,9 +89,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = proc toNode(t: PType, i: TLineInfo): PNode = result = newNodeIT(nkType, i, t) -const +const # these are types that use the bracket syntax for instantiation - # they can be subjected to the type traits `genericHead` and + # they can be subjected to the type traits `genericHead` and # `Uninstantiated` tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation, tyUserTypeClassInst} @@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType = of tyCompositeTypeClass: uninstantiate t.sons[1] else: t -proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = - var typ = operand.skipTypes({tyTypeDesc}) +proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = + const skippedTypes = {tyTypeDesc} + let trait = traitCall[0] + internalAssert trait.kind == nkSym + var operand = operand.skipTypes(skippedTypes) + + template operand2: PType = + traitCall.sons[2].typ.skipTypes({tyTypeDesc}) + + template typeWithSonsResult(kind, sons): PNode = + newTypeWithSons2(kind, context, sons).toNode(traitCall.info) + case trait.sym.name.s + of "or", "|": + return typeWithSonsResult(tyOr, @[operand, operand2]) + of "and": + return typeWithSonsResult(tyAnd, @[operand, operand2]) + of "not": + return typeWithSonsResult(tyNot, @[operand]) of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) + result = newStrNode(nkStrLit, operand.typeToString(preferName)) result.typ = newType(tyString, context) - result.info = trait.info + result.info = traitCall.info of "arity": - result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc)) + result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc)) result.typ = newType(tyInt, context) - result.info = trait.info + result.info = traitCall.info of "genericHead": - var res = uninstantiate(typ) - if res == typ and res.kind notin tyMagicGenerics: - localError(trait.info, + var res = uninstantiate(operand) + if res == operand and res.kind notin tyMagicGenerics: + localError(traitCall.info, "genericHead expects a generic type. The given type was " & - typeToString(typ)) - return newType(tyError, context).toNode(trait.info) - result = res.base.toNode(trait.info) + typeToString(operand)) + return newType(tyError, context).toNode(traitCall.info) + result = res.base.toNode(traitCall.info) of "stripGenericParams": - result = uninstantiate(typ).toNode(trait.info) + result = uninstantiate(operand).toNode(traitCall.info) else: internalAssert false @@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = if t.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n[0], t, getCurrOwner(c)) + result = evalTypeTrait(n, t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/lib/system.nim b/lib/system.nim index 82d3bb7f7..f9ae5b552 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -49,6 +49,9 @@ type cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr`` ## operator to get a pointer to a variable + + typedesc* {.magic: TypeDesc.} ## meta type to denote a type description + const on* = true ## alias for ``true`` off* = false ## alias for ``false`` @@ -56,6 +59,18 @@ const {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} +proc `|` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `or` meta class + +proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `or` meta class + +proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `and` meta class + +proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `not` meta class + type Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer, ## bool, character, and enumeration types @@ -66,11 +81,11 @@ type `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type `nil` {.magic: "Nil".} + expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates) - ## **Deprecated** since version 0.15. Use ``untyped`` instead. + ## **Deprecated** since version 0.15. Use ``untyped`` instead. stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates) ## **Deprecated** since version 0.15. Use ``typed`` instead. - typedesc* {.magic: TypeDesc.} ## meta type to denote a type description void* {.magic: "VoidType".} ## meta type to denote the absence of any type auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type From c981284ddcbfe192b3554c9730b5fa6fc1b35a17 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 17:40:57 +0300 Subject: [PATCH 02/19] a simple way to simulate covariance in generic types --- compiler/sigmatch.nim | 7 ++++++- tests/generics/tfakecovariance.nim | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/generics/tfakecovariance.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d6a0c6382..db3775cca 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -956,6 +956,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = case a.kind of tyOr: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[int|string] vs seq[number] # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): @@ -964,15 +966,18 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = let x = typeRel(c, f, branch, false) if x == isNone: return isNone if x < result: result = x + return of tyAnd: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: let x = typeRel(c, f, branch, false) if x != isNone: return if x >= isGeneric: isGeneric else: x - result = isNone + return isNone of tyNot: case f.kind diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim new file mode 100644 index 000000000..835acd9d3 --- /dev/null +++ b/tests/generics/tfakecovariance.nim @@ -0,0 +1,11 @@ +type + BaseObj = object of RootObj + DerivedObj = object of BaseObj + + Container[T] = object + +proc doSomething(c: Container[BaseObj or DerivedObj]) = discard + +var t: Container[DerivedObj] +doSomething t + From 72f42ff95f08e256344913f1577a17ba46a374ce Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 18:01:04 +0300 Subject: [PATCH 03/19] more advanced fake covariance --- tests/generics/tfakecovariance.nim | 51 ++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim index 835acd9d3..bca285283 100644 --- a/tests/generics/tfakecovariance.nim +++ b/tests/generics/tfakecovariance.nim @@ -1,11 +1,56 @@ +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + type BaseObj = object of RootObj DerivedObj = object of BaseObj + NonDerivedObj = object Container[T] = object -proc doSomething(c: Container[BaseObj or DerivedObj]) = discard +var base: BaseObj +var derived: DerivedObj +var nonDerived: NonDerivedObj -var t: Container[DerivedObj] -doSomething t +var baseContainer: Container[BaseObj] +var derivedContainer: Container[DerivedObj] +var nonDerivedContainer: Container[NonDerivedObj] + +# We can fake covariance by listing some specific derived types that +# will be allowed with our overload. This is not a real covariance, +# because there will be multiple instantiations of the proc, but for +# many purposes, it will suffice: + +proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard + +accept wantsSpecificContainers(baseContainer) +accept wantsSpecificContainers(derivedContainer) + +reject wantsSpecificContainers(nonDerivedContainer) +reject wantsSpecificContainers(derived) + +# Now, let's make a more general solution able to catch all derived types: + +type + DerivedFrom[T] = concept type A + var derived: ref A + var base: ref T = derived + +proc wantsDerived(x: DerivedFrom[BaseObj]) = discard + +accept wantsDerived(base) +accept wantsDerived(derived) + +reject wantsDerived(nonDerived) +reject wantsDerived(baseContainer) + +proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard + +accept wantsDerivedContainer(baseContainer) +accept wantsDerivedContainer(derivedContainer) + +reject wantsDerivedContainer(nonDerivedContainer) From 5c678e284862e6b52b0dd4118ef857d7b6b17ef9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 18:26:33 +0300 Subject: [PATCH 04/19] alternative fake covariance based on converters --- tests/generics/tfakecovariance.nim | 26 ++++++++++++++++++++++++-- tests/generics/tptrinheritance.nim | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim index bca285283..0920cb504 100644 --- a/tests/generics/tfakecovariance.nim +++ b/tests/generics/tfakecovariance.nim @@ -35,8 +35,8 @@ reject wantsSpecificContainers(derived) # Now, let's make a more general solution able to catch all derived types: type - DerivedFrom[T] = concept type A - var derived: ref A + DerivedFrom[T] = concept type D + var derived: ref D var base: ref T = derived proc wantsDerived(x: DerivedFrom[BaseObj]) = discard @@ -54,3 +54,25 @@ accept wantsDerivedContainer(derivedContainer) reject wantsDerivedContainer(nonDerivedContainer) +# The previous solutions were solving the problem for a single overload. +# Let's solve it for multiple overloads by introducing a converter: + +type + OtherContainer[T] = object + +proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard +proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard + +converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard + +block: + var baseContainer: OtherContainer[BaseObj] + var derivedContainer: OtherContainer[DerivedObj] + var nonDerivedContainer: OtherContainer[NonDerivedObj] + + accept wantsBaseContainer1(derivedContainer) + reject wantsBaseContainer1(nonDerivedContainer) + + accept wantsBaseContainer2(derivedContainer) + reject wantsBaseContainer2(nonDerivedContainer) + diff --git a/tests/generics/tptrinheritance.nim b/tests/generics/tptrinheritance.nim index 1e1115fa5..221b8777b 100644 --- a/tests/generics/tptrinheritance.nim +++ b/tests/generics/tptrinheritance.nim @@ -10,7 +10,7 @@ proc newMutableArrayAbstract*(): NSMutableArrayAbstract = discard template newMutableArray*(T: typedesc): NSMutableArray[T] = cast[NSMutableArray[T]](newMutableArrayAbstract()) -proc writeObjects*(p: NSPasteboard, o: ptr NSArray[NSPasteboardItem]) = discard +proc writeObjects*(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard let a = newMutableArray NSPasteboardItem var x: NSMutableArray[NSPasteboardItem] From fa52cff82666cc404e71acdc967444a8d9fab31c Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 18:38:08 +0300 Subject: [PATCH 05/19] hold back the new | operator for types as it creates mysterious problems fro tests/trmacros/tor --- lib/system.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index f9ae5b552..4dcb39c2a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -59,9 +59,6 @@ const {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} -proc `|` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} - ## Constructs an `or` meta class - proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `or` meta class From 9107e551f1a617d5c3d0c3dfef44c91422a1dd07 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 28 Apr 2017 19:09:11 +0300 Subject: [PATCH 06/19] close #5726 Turned out that the old code was wrong. I'm not sure why it used to work. `response.body` properly resolves to an async proc defined in the httpclient module with the following signature: proc body*(response: AsyncResponse): Future[string] {.async.} Perhaps the old code was somehow matching the body field of the `AsyncResponse` object, which is marked as private. --- web/bountysource.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/bountysource.nim b/web/bountysource.nim index 1d47cea56..8d4f36bf0 100644 --- a/web/bountysource.nim +++ b/web/bountysource.nim @@ -29,17 +29,19 @@ proc newBountySource(team, token: string): BountySource = result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters" result.client.headers["Origin"] = "https://salt.bountysource.com/" +import typetraits + proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = let response = await self.client.get(apiUrl & "/supporters?order=monthly&per_page=200&team_slug=" & self.team) doAssert response.status.startsWith($Http200) - return parseJson(response.body) + return parseJson(await response.body) proc getGithubUser(username: string): Future[JsonNode] {.async.} = let client = newAsyncHttpClient() let response = await client.get(githubApiUrl & "/users/" & username) if response.status.startsWith($Http200): - return parseJson(response.body) + return parseJson(await response.body) else: echo("Could not get Github user: ", username, ". ", response.status) return nil From 622100adb6deb75cfb0fad4e4e94af9f0d7a232c Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 30 Apr 2017 21:57:13 +0300 Subject: [PATCH 07/19] close #5757 --- tests/stdlib/tmarshal.nim | 24 +++++++++++++++++++++++- web/bountysource.nim | 2 -- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index b05cb5a10..6a53a2964 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,7 +1,10 @@ discard """ output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} true -true''' +true +alpha 100 +omega 200 +''' """ import marshal @@ -83,3 +86,22 @@ var instance1 = Person(name: "Cletus", age: 12, echo($$instance1) echo(to[Person]($$instance1).bio == instance1.bio) echo(to[Person]($$instance1).blob == instance1.blob) + +# bug 5757 + +type + Something = object + x: string + y: int + +var data1 = """{"x": "alpha", "y": 100}""" +var data2 = """{"x": "omega", "y": 200}""" + +var r = to[Something](data1) + +echo r.x, " ", r.y + +r = to[Something](data2) + +echo r.x, " ", r.y + diff --git a/web/bountysource.nim b/web/bountysource.nim index 8d4f36bf0..5dfdb4497 100644 --- a/web/bountysource.nim +++ b/web/bountysource.nim @@ -29,8 +29,6 @@ proc newBountySource(team, token: string): BountySource = result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters" result.client.headers["Origin"] = "https://salt.bountysource.com/" -import typetraits - proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = let response = await self.client.get(apiUrl & "/supporters?order=monthly&per_page=200&team_slug=" & self.team) From 2a3455259618723b9a748b16e2fccf2a06b0c9cb Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 30 Apr 2017 22:28:20 +0300 Subject: [PATCH 08/19] fix 5756 --- compiler/ccgexprs.nim | 2 +- tests/generics/tgenericconst.nim | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/generics/tgenericconst.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f981f9595..f2c6f076c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2203,7 +2203,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) = let field = obj.sym for i in 1.. Date: Mon, 8 May 2017 21:45:37 +0300 Subject: [PATCH 09/19] support for external types with covariant generic params --- compiler/ast.nim | 4 ++++ compiler/condsyms.nim | 1 + compiler/msgs.nim | 2 ++ compiler/parser.nim | 9 ++++++++- compiler/pbraces.nim | 5 +++++ compiler/semtypes.nim | 16 ++++++++++++++-- lib/core/macros.nim | 2 ++ tests/errmsgs/tinvalidinout.nim | 26 ++++++++++++++++++++++++++ 8 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/errmsgs/tinvalidinout.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 5b68e9712..d4ffbcfa4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -212,6 +212,8 @@ type nkIteratorTy, # iterator type nkSharedTy, # 'shared T' # we use 'nkPostFix' for the 'not nil' addition + nkInTy, # prefix `in` used to mark contravariant types + nkOutTy, # prefix `out` used to mark covariant types nkEnumTy, # enum body nkEnumFieldDef, # `ident = expr` in an enumeration nkArgList, # argument list @@ -268,6 +270,8 @@ type sfDiscardable, # returned value may be discarded implicitly sfOverriden, # proc is overriden sfGenSym # symbol is 'gensym'ed; do not add to symbol table + sfCovariant # covariant generic param + sfContravariant # contravariant generic param TSymFlags* = set[TSymFlag] diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4303fd6c8..a4f47ac72 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -103,3 +103,4 @@ proc initDefines*() = defineSymbol("nimNewShiftOps") defineSymbol("nimDistros") defineSymbol("nimHasCppDefine") + defineSymbol("nimGenericInOutFlags") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3a97f1ed2..1842ce1de 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -113,6 +113,7 @@ type errGenericLambdaNotAllowed, errProcHasNoConcreteType, errCompilerDoesntSupportTarget, + errInOutFlagNotExtern, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -380,6 +381,7 @@ const "of the generic paramers can be inferred from the expected signature.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", + errInOutFlagNotExtern: "`in` and `out` flags can be used only with imported types", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", diff --git a/compiler/parser.nim b/compiler/parser.nim index e4c04c0f7..f4d1aca15 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1523,6 +1523,13 @@ proc parseGenericParam(p: var TParser): PNode = # progress guaranteed while true: case p.tok.tokType + of tkIn, tkOut: + let kind = if p.tok.tokType == tkIn: nkInTy + else: nkOutTy + a = newNodeP(kind, p) + getTok(p) + expectIdent(p) + a.addSon(parseSymbol(p)) of tkSymbol, tkAccent: a = parseSymbol(p) if a.kind == nkEmpty: return @@ -1551,7 +1558,7 @@ proc parseGenericParamList(p: var TParser): PNode = getTok(p) optInd(p, result) # progress guaranteed - while p.tok.tokType in {tkSymbol, tkAccent}: + while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}: var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index b59fbc6cf..fa4ccc139 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode = result = newNodeP(nkIdentDefs, p) while true: case p.tok.tokType + of tkIn, tkOut: + let t = p.tok.tokType + getTok(p) + expectIdent(p) + a = parseSymbol(p) of tkSymbol, tkAccent: a = parseSymbol(p) if a.kind == nkEmpty: return diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 16066da91..5cc5a1ecf 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1584,10 +1584,22 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # type for each generic param. the index # of the parameter will be stored in the # attached symbol. + var paramName = a.sons[j] + var covarianceFlag = sfPure + + if paramName.kind in {nkInTy, nkOutTy}: + if father == nil or sfImportc notin father.sym.flags: + localError(paramName.info, errInOutFlagNotExtern) + paramName = paramName[0] + covarianceFlag = if paramName.kind == nkInTy: sfContravariant + else: sfCovariant + var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: - newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) + newSymG(skGenericParam, paramName, c).linkTo(finalType) else: - newSymG(skType, a.sons[j], c).linkTo(finalType) + newSymG(skType, paramName, c).linkTo(finalType) + + if covarianceFlag != sfPure: s.flags.incl(covarianceFlag) if def.kind != nkEmpty: s.ast = def if father != nil: addSonSkipIntLit(father, s.typ) s.position = result.len diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 83776f16b..5e0d4030d 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -68,6 +68,8 @@ type nnkProcTy, nnkIteratorTy, # iterator type nnkSharedTy, # 'shared T' + nnkInTy, + nnkOutTy, nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim new file mode 100644 index 000000000..463c9951a --- /dev/null +++ b/tests/errmsgs/tinvalidinout.nim @@ -0,0 +1,26 @@ +discard """ +cmd: "nim check $file" +errormsg: "`in` and `out` flags can be used only with imported types" +nimout: ''' +tinvalidinout.nim(14, 7) Error: `in` and `out` flags can be used only with imported types +tinvalidinout.nim(17, 9) Error: `in` and `out` flags can be used only with imported types +tinvalidinout.nim(18, 9) Error: `in` and `out` flags can be used only with imported types +''' +""" + +type + Foo {.header: "foo.h", importcpp.} [in T] = object + + Bar[out X] = object + x: int + +proc f1[in T](x: T) = discard +proc f2[in T](x: T) {.importc: "f", header: "foo.h"} + +var + f: Foo[int] + b: Bar[string] + +f1 f +f2 b + From 597ea0881505946ba83f5944bf9cd17cb6262f59 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 11:46:25 +0300 Subject: [PATCH 10/19] covariance for generic importc types --- tests/typerel/tcovariancerules.nim | 253 +++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 tests/typerel/tcovariancerules.nim diff --git a/tests/typerel/tcovariancerules.nim b/tests/typerel/tcovariancerules.nim new file mode 100644 index 000000000..067fe08ac --- /dev/null +++ b/tests/typerel/tcovariancerules.nim @@ -0,0 +1,253 @@ +discard """ +cmd: "nim check $file" +""" + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +type + Animal = object of TObject + x: int + + Dog = object of Animal + y: int + + Cat = object of Animal + z: int + + AnimalRef = ref Animal + AnimalPtr = ptr Animal + +var dog = new(Dog) +var cat = new(Cat) + +proc wantsRefArray(x: array[2, ref Animal]) = discard + +accept: + wantsRefArray([AnimalRef(dog), AnimalRef(dog)]) + wantsRefArray([AnimalRef(cat), AnimalRef(dog)]) + wantsRefArray([AnimalRef(cat), dog]) + + # there is a special rule that detects the base + # type of polymorphic arrays + wantsRefArray([cat, dog]) + +reject: + # But the current lack of covariance kicks in + # when we try to pass a derived type array + wantsRefArray([cat, cat]) + +var animalRefArray: array[2, ref Animal] + +accept: + animalRefArray = [AnimalRef(dog), AnimalRef(dog)] + animalRefArray = [AnimalRef(cat), dog] + +reject: + animalRefArray = [dog, dog] + +accept: + var animal: AnimalRef = dog + animal = cat + +var vdog: Dog +var vcat: Cat + +reject: + vcat = vdog + +# XXX: The next two cases seem incosistent, perhaps we should change the rules +accept: + # truncating copies are allowed + var vanimal: Animal = vdog + vanimal = vdog + +reject: + # truncating copies are not allowed with arrays + var vanimalArray: array[2, Animal] + var vdogArray = [vdog, vdog] + vanimalArray = vdogArray + +accept: + # a more explicit version of a truncating copy that + # should probably always remain allowed + var vnextnimal: Animal = Animal(vdog) + +proc wantsRefSeq(x: seq[AnimalRef]) = discard + +accept: + wantsRefSeq(@[AnimalRef(dog), AnimalRef(dog)]) + wantsRefSeq(@[AnimalRef(cat), AnimalRef(dog)]) + wantsRefSeq(@[AnimalRef(cat), dog]) + wantsRefSeq(@[cat, dog]) + +reject: + wantsRefSeq(@[cat, cat]) + +var animalRefSeq: seq[ref Animal] + +accept: + animalRefArray = [AnimalRef(dog), AnimalRef(dog)] + animalRefArray = [AnimalRef(cat), dog] + +reject: + animalRefArray = [dog, dog] + +var pdog: ptr Dog +var pcat: ptr Cat + +proc wantsPointer(x: ptr Animal) = + discard + +accept: + wantsPointer pdog + wantsPointer pcat + +# covariance should be disabled when var is involved +proc wantsVarPointer1(x: var ptr Animal) = + discard + +proc wantsVarPointer2(x: var AnimalPtr) = + discard + +reject wantsVarPointer1(pdog) +reject wantsVarPointer2(pcat) + +proc usesVarRefSeq(x: var seq[AnimalRef]) = discard +proc usesVarRefArray(x: var array[2, AnimalRef]) = discard + +reject: + var catsSeq = @[cat, cat] + usesVarRefSeq catsSeq + +reject: + var catsArray = [cat, cat] + usesVarRefArray catsArray + +# covariance may be allowed for certain extern types + +{.emit: """ +template struct FN { typedef void (*type)(T); }; +template struct ARR { typedef T type[2]; }; +""".} + +type + MyPtr {.importcpp: "'0 *"} [out T] = distinct ptr T + MySeq {.importcpp: "ARR<'0>::type"} [out T] = object + MyAction {.importcpp: "FN<'0>::type"} [in T] = object + +var + cAnimal: MyPtr[Animal] + cDog: MyPtr[Dog] + cCat: MyPtr[Cat] + + cAnimalFn: MyAction[Animal] + cCatFn: MyAction[Cat] + cDogFn: MyAction[Dog] + + cRefAnimalFn: MyAction[ref Animal] + cRefCatFn: MyAction[ref Cat] + cRefDogFn: MyAction[ref Dog] + +accept: + cAnimal = cDog + cAnimal = cCat + + cDogFn = cAnimalFn + cCatFn = cAnimalFn + + cRefDogFn = cRefAnimalFn + cRefCatFn = cRefAnimalFn + +reject: cDogFn = cRefAnimalFn +reject: cCatFn = cRefAnimalFn + +reject: cCat = cDog +reject: cAnimalFn = cDogFn +reject: cAnimalFn = cCatFn +reject: cRefAnimalFn = cRefDogFn +reject: cRefAnimalFn = cRefCatFn +reject: cRefAnimalFn = cDogFn + +var + ptrPtrDog: ptr ptr Dog + ptrPtrAnimal: ptr ptr Animal + +reject: ptrPtrDog = ptrPtrAnimal + +type + RefAlias[T] = ref T + +# Try to break the rules by introducing some tricky +# double indirection types: +var + cPtrRefAnimal: MyPtr[ref Animal] + cPtrRefDog: MyPtr[ref Dog] + + cPtrAliasRefAnimal: MyPtr[RefAlias[Animal]] + cPtrAliasRefDog: MyPtr[RefAlias[Dog]] + + cDoublePtrAnimal: MyPtr[MyPtr[Animal]] + cDoublePtrDog: MyPtr[MyPtr[Dog]] + +reject: cPtrRefAnimal = cPtrRefDog +reject: cDoublePtrAnimal = cDoublePtrDog +reject: cRefAliasPtrAnimal = cRefAliasPtrDog +reject: cPtrRefAnimal = cRefAliasPtrDog +reject: cPtrAliasRefAnimal = cPtrRefDog + +var + # Array and Sequence types are covariant only + # when instantiated with ref or ptr types: + cAnimals: MySeq[ref Animal] + cDogs: MySeq[ref Dog] + + # "User-defined" pointer types should be OK too: + cAnimalPtrSeq: MySeq[MyPtr[Animal]] + cDogPtrSeq: MySeq[MyPtr[Dog]] + + # Value types shouldn't work: + cAnimalValues: MySeq[Animal] + cDogValues: MySeq[Dog] + + # Double pointer types should not work either: + cAnimalRefPtrSeq: MySeq[ref MyPtr[Animal]] + cDogRefPtrSeq: MySeq[ref MyPtr[Dog]] + cAnimalPtrPtrSeq: MySeq[ptr ptr Animal] + cDogPtrPtrSeq: MySeq[ptr ptr Dog] + +accept: + cAnimals = cDogs + cAnimalPtrSeq = cDogPtrSeq + +reject: cAnimalValues = cDogValues +reject: cAnimalRefPtrSeq = cDogRefPtrSeq +reject: cAnimalPtrPtrSeq = cDogPtrPtrSeq + +proc wantsAnimalSeq(x: MySeq[Animal]) = discard +proc wantsAnimalRefSeq(x: MySeq[ref Animal]) = discard +proc modifiesAnimalRefSeq(x: var MySeq[ref Animal]) = discard +proc usesAddressOfAnimalRefSeq(x: ptr MySeq[ref Animal]) = discard + +accept wantsAnimalSeq(cAnimalValues) +reject wantsAnimalSeq(cDogValues) +reject wantsAnimalSeq(cAnimals) + +reject wantsAnimalRefSeq(cAnimalValues) +reject wantsAnimalRefSeq(cDogValues) +accept wantsAnimalRefSeq(cAnimals) +accept wantsAnimalRefSeq(cDogs) + +reject modifiesAnimalRefSeq(cAnimalValues) +reject modifiesAnimalRefSeq(cDogValues) +accept modifiesAnimalRefSeq(cAnimals) +reject modifiesAnimalRefSeq(cDogs) + +reject usesAddressOfAnimalRefSeq(addr cAnimalValues) +reject usesAddressOfAnimalRefSeq(addr cDogValues) +accept usesAddressOfAnimalRefSeq(addr cAnimals) +reject usesAddressOfAnimalRefSeq(addr cDogs) + From a802d724525628879dd7eda0c97436ccb4084952 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 12:46:09 +0300 Subject: [PATCH 11/19] doh, forgot to add all files in the previous commit --- compiler/ast.nim | 3 +- compiler/semcall.nim | 2 +- compiler/semtypes.nim | 5 +++- compiler/sigmatch.nim | 70 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d4ffbcfa4..4bd76e41e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -270,7 +270,8 @@ type sfDiscardable, # returned value may be discarded implicitly sfOverriden, # proc is overriden sfGenSym # symbol is 'gensym'ed; do not add to symbol table - sfCovariant # covariant generic param + sfCovariant # covariant generic param mimicing seq/array type + sfStrongCovariant # covariant generic param mimicing ptr type sfContravariant # contravariant generic param TSymFlags* = set[TSymFlag] diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1089ab7db..4fa4f7f32 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -429,7 +429,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = for i in 1..sonsLen(n)-1: let formal = s.ast.sons[genericParamsPos].sons[i-1].typ let arg = n[i].typ - let tm = typeRel(m, formal, arg, true) + let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5cc5a1ecf..ad7e53a4d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -136,6 +136,9 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) var t = semTypeNode(c, n.lastSon, nil) + if c.inGenericContext > 0: + if t.sym != nil and sfCovariant in t.sym.flags: + t.sym.flags.incl sfStrongCovariant if t.kind == tyTypeDesc and tfUnresolved notin t.flags: t = t.base result = newOrPrevType(kind, prev, c) @@ -1590,9 +1593,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if paramName.kind in {nkInTy, nkOutTy}: if father == nil or sfImportc notin father.sym.flags: localError(paramName.info, errInOutFlagNotExtern) - paramName = paramName[0] covarianceFlag = if paramName.kind == nkInTy: sfContravariant else: sfCovariant + paramName = paramName[0] var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: newSymG(skGenericParam, paramName, c).linkTo(finalType) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index db3775cca..d68fe6a41 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -69,6 +69,12 @@ type mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type + TTypeRelFlag* = enum + trDontBind + trNoCovariance + + TTypeRelFlags* = set[TTypeRelFlag] + TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -296,7 +302,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; add(result, argTypeToString(arg, prefer)) if i != sonsLen(n) - 1: add(result, ", ") -proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + proc concreteType(c: TCandidate, t: PType): PType = case t.kind of tyNil: @@ -860,7 +868,28 @@ template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: result = isNone -proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = +proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = + # this proc is always called for a pair of matching types + assert f.kind == a.kind + + template baseTypesCheck(lhs, rhs: PType): bool = + lhs.kind notin {tyPtr, tyRef, tyVar} and + typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype + + case f.kind + of tyRef, tyPtr: + return baseTypesCheck(f.base, a.base) + of tyGenericInst: + let body = f.base + return body == a.base and + a.sonsLen == 3 and + sfStrongCovariant in body.sons[0].sym.flags and + baseTypesCheck(f.sons[1], a.sons[1]) + else: + return false + +proc typeRel(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation = # typeRel can be used to establish various relationships between types: # # 1) When used with concrete types, it will check for type equivalence @@ -927,6 +956,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isEqual return + template doBind: bool = trDontBind notin flags + # var and static arguments match regular modifier-free types var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even @@ -963,7 +994,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x return @@ -974,7 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x return isNone @@ -1001,7 +1032,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: # consider this: 'var g: Node' *within* a concept where 'Node' # is a concept too (tgraph) - let x = typeRel(c, a, f, false) + let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric else: discard @@ -1047,7 +1078,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFloat128: result = handleFloatRange(f, a) of tyVar: if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) - else: result = typeRel(c, f.base, aOrig) + else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: case a.kind @@ -1163,7 +1194,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if a.len < f.len: return isNone for i in 0..f.len-2: if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone - result = typeRel(c, f.lastSon, a.lastSon) + result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance}) subtypeCheck() if result <= isConvertible: result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: @@ -1235,15 +1266,30 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = var m = c if a.kind == tyGenericInst: if roota.base == rootf.base: + let nextFlags = flags + {trNoCovariance} + var hasCovariance = false for i in 1 .. rootf.sonsLen-2: let ff = rootf.sons[i] let aa = roota.sons[i] - result = typeRel(c, ff, aa) - if result notin {isEqual, isGeneric}: return isNone - # if ff.kind == tyRange and result != isEqual: return isNone + result = typeRel(c, ff, aa, nextFlags) + if result notin {isEqual, isGeneric}: + if trNoCovariance notin flags and ff.kind == aa.kind: + let paramFlags = rootf.base.sons[i-1].sym.flags + hasCovariance = + if sfCovariant in paramFlags: + if sfStrongCovariant in paramFlags: + ff.kind notin {tyRef, tyPtr} and result == isSubtype + else: + isCovariantPtr(c, ff, aa) + else: + sfContravariant in paramFlags and + typeRel(c, aa, ff) == isSubtype + if hasCovariance: + continue + return isNone if prev == nil: put(c, f, a) - result = isGeneric + result = if hasCovariance: isGeneric else: isGeneric else: let fKind = rootf.lastSon.kind if fKind in {tyAnd, tyOr}: @@ -1455,7 +1501,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone else: if f.sonsLen > 0 and f.sons[0].kind != tyNone: - result = typeRel(c, f.lastSon, a, false) + result = typeRel(c, f.lastSon, a, flags + {trDontBind}) if doBind and result notin {isNone, isGeneric}: let concrete = concreteType(c, a) if concrete == nil: return isNone From f4e73344d4b6bcc7282d363a008307d5482f2a22 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 18:42:46 +0300 Subject: [PATCH 12/19] covariance for arrays and sequences --- compiler/sigmatch.nim | 52 ++++-- lib/system.nim | 1 + tests/typerel/tcovariancerules.nim | 244 ++++++++++++++++++++++++----- 3 files changed, 247 insertions(+), 50 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d68fe6a41..c2a9b78bf 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -889,7 +889,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = return false proc typeRel(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation = + flags: TTypeRelFlags = {}): TTypeRelation = # typeRel can be used to establish various relationships between types: # # 1) When used with concrete types, it will check for type equivalence @@ -1092,9 +1092,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, fRange = a else: fRange = prev - result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), - a.sons[1].skipTypes({tyTypeDesc})) - if result < isGeneric: return isNone + let ff = f.sons[1].skipTypes({tyTypeDesc}) + let aa = a.sons[1].skipTypes({tyTypeDesc}) + result = typeRel(c, ff, aa) + if result < isGeneric: + if nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isSubtype + else: + return isNone if fRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, fRange, a) @@ -1113,20 +1121,31 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if tfOldSchoolExprStmt in f.sons[0].flags: if f.sons[0].kind == tyExpr: return elif f.sons[0].kind == tyStmt: return + + template matchArrayOrSeq(aBase: PType) = + let ff = f.base + let aa = aBase + let baseRel = typeRel(c, ff, aa) + if baseRel >= isGeneric: + result = isConvertible + elif nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isConvertible + case a.kind of tyOpenArray, tyVarargs: result = typeRel(c, base(f), base(a)) if result < isGeneric: result = isNone of tyArray: if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): - result = isSubtype - elif typeRel(c, base(f), a.sons[1]) >= isGeneric: - result = isConvertible + return isSubtype + matchArrayOrSeq(a.sons[1]) of tySequence: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): - result = isConvertible - elif typeRel(c, base(f), a.sons[0]) >= isGeneric: - result = isConvertible + return isConvertible + matchArrayOrSeq(a.sons[0]) of tyString: if f.kind == tyOpenArray: if f.sons[0].kind == tyChar: @@ -1141,8 +1160,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): result = isSubtype else: - result = typeRel(c, f.sons[0], a.sons[0]) - if result < isGeneric: result = isNone + let ff = f.sons[0] + let aa = a.sons[0] + result = typeRel(c, ff, aa) + if result < isGeneric: + if nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isSubtype + else: + result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion of tyNil: result = f.allowsNil diff --git a/lib/system.nim b/lib/system.nim index 4dcb39c2a..ad808d8c8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1344,6 +1344,7 @@ const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") + nimEnableCovariance* = defined(nimEnableCovariance) # or true when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS diff --git a/tests/typerel/tcovariancerules.nim b/tests/typerel/tcovariancerules.nim index 067fe08ac..7cd5d5066 100644 --- a/tests/typerel/tcovariancerules.nim +++ b/tests/typerel/tcovariancerules.nim @@ -1,5 +1,29 @@ discard """ -cmd: "nim check $file" +cmd: "nim cpp $file" +output: ''' +cat +cat +dog +dog +cat +cat +dog +dog X +cat +cat +dog +dog +dog value +cat value +dog value +cat value +dog +dog +dog value +cat value +dog 1 +dog 2 +''' """ template accept(x) = @@ -8,9 +32,20 @@ template accept(x) = template reject(x) = static: assert(not compiles(x)) +import macros + +macro skipElse(n: untyped): typed = n[0] + +template acceptWithCovariance(x, otherwise): typed = + when nimEnableCovariance: + x + else: + reject(x) + skipElse(otherwise) + type Animal = object of TObject - x: int + x: string Dog = object of Animal y: int @@ -21,24 +56,94 @@ type AnimalRef = ref Animal AnimalPtr = ptr Animal -var dog = new(Dog) -var cat = new(Cat) + RefAlias[T] = ref T -proc wantsRefArray(x: array[2, ref Animal]) = discard +var dog = new(Dog) +dog.x = "dog" + +var cat = new(Cat) +cat.x = "cat" + +proc makeDerivedRef(x: string): ref Dog = + new(result) + result.x = x + +proc makeDerived(x: string): Dog = + result.x = x + +var covariantSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] +var nonCovariantSeq = @[makeDerived("dog 1"), makeDerived("dog 2")] +var covariantArr = [makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] +var nonCovariantArr = [makeDerived("dog 1"), makeDerived("dog 2")] + +proc wantsCovariantSeq1(s: seq[ref Animal]) = + for a in s: echo a.x + +proc wantsCovariantSeq2(s: seq[AnimalRef]) = + for a in s: echo a.x + +proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) = + for a in s: echo a.x + +proc wantsCovariantOperArray(s: openarray[ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantOperArray(s: var openarray[ref Animal]) = + for a in s: echo a.x + +proc modifiesDerivedOperArray(s: var openarray[ref Dog]) = + for a in s: echo a.x + +proc wantsNonCovariantOperArray(s: openarray[Animal]) = + for a in s: echo a.x + +proc wantsCovariantArray(s: array[2, ref Animal]) = + for a in s: echo a.x + +proc wantsNonCovariantSeq(s: seq[Animal]) = + for a in s: echo a.x + +proc wantsNonCovariantArray(s: array[2, Animal]) = + for a in s: echo a.x + +proc modifiesCovariantSeq(s: var seq[ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantArray(s: var array[2, ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantSeq(s: ptr seq[ref Animal]) = + for a in s[]: echo a.x + +proc modifiesCovariantArray(s: ptr array[2, ref Animal]) = + for a in s[]: echo a.x + +proc modifiesDerivedSeq(s: var seq[ref Dog]) = + for a in s: echo a.x + +proc modifiesDerivedArray(s: var array[2, ref Dog]) = + for a in s: echo a.x + +proc modifiesDerivedSeq(s: ptr seq[ref Dog]) = + for a in s[]: echo a.x + +proc modifiesDerivedArray(s: ptr array[2, ref Dog]) = + for a in s[]: echo a.x accept: - wantsRefArray([AnimalRef(dog), AnimalRef(dog)]) - wantsRefArray([AnimalRef(cat), AnimalRef(dog)]) - wantsRefArray([AnimalRef(cat), dog]) + wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantArray([AnimalRef(cat), dog]) # there is a special rule that detects the base # type of polymorphic arrays - wantsRefArray([cat, dog]) + wantsCovariantArray([cat, dog]) -reject: - # But the current lack of covariance kicks in - # when we try to pass a derived type array - wantsRefArray([cat, cat]) +acceptWithCovariance: + wantsCovariantArray([cat, cat]) +else: + echo "cat" + echo "cat" var animalRefArray: array[2, ref Animal] @@ -46,15 +151,21 @@ accept: animalRefArray = [AnimalRef(dog), AnimalRef(dog)] animalRefArray = [AnimalRef(cat), dog] -reject: +acceptWithCovariance: animalRefArray = [dog, dog] + wantsCovariantArray animalRefArray +else: + echo "dog" + echo "dog" accept: var animal: AnimalRef = dog animal = cat var vdog: Dog +vdog.x = "dog value" var vcat: Cat +vcat.x = "cat value" reject: vcat = vdog @@ -79,22 +190,93 @@ accept: proc wantsRefSeq(x: seq[AnimalRef]) = discard accept: - wantsRefSeq(@[AnimalRef(dog), AnimalRef(dog)]) - wantsRefSeq(@[AnimalRef(cat), AnimalRef(dog)]) - wantsRefSeq(@[AnimalRef(cat), dog]) - wantsRefSeq(@[cat, dog]) + wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq1(@[AnimalRef(cat), dog]) + wantsCovariantSeq1(@[cat, dog]) -reject: - wantsRefSeq(@[cat, cat]) + wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq2(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq2(@[AnimalRef(cat), dog]) + wantsCovariantSeq2(@[cat, dog]) + + wantsCovariantSeq3(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq3(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq3(@[AnimalRef(cat), dog]) + wantsCovariantSeq3(@[cat, dog]) + + wantsCovariantOperArray([cat, dog]) + +acceptWithCovariance: + wantsCovariantSeq1(@[cat, cat]) + wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")]) + # XXX: wantsCovariantSeq3(@[cat, cat]) + + wantsCovariantOperArray(@[cat, cat]) + wantsCovariantOperArray([dog, dog]) +else: + echo "cat" + echo "cat" + echo "dog" + echo "dog X" + echo "cat" + echo "cat" + echo "dog" + echo "dog" + +var dogRefs = @[dog, dog] +var dogRefsArray = [dog, dog] +var animalRefs = @[dog, cat] + +accept: + modifiesDerivedArray(dogRefsArray) + modifiesDerivedSeq(dogRefs) + +reject modifiesCovariantSeq(dogRefs) +reject modifiesCovariantSeq(addr(dogRefs)) +reject modifiesCovariantSeq(dogRefs.addr) + +reject modifiesCovariantArray([dog, dog]) +reject modifiesCovariantArray(dogRefsArray) +reject modifiesCovariantArray(addr(dogRefsArray)) +reject modifiesCovariantArray(dogRefsArray.addr) + +var dogValues = @[vdog, vdog] +var dogValuesArray = [vdog, vdog] +var animalValues = @[Animal(vdog), Animal(vcat)] +var animalValuesArray = [Animal(vdog), Animal(vcat)] + +wantsNonCovariantSeq animalValues +wantsNonCovariantArray animalValuesArray + +reject wantsNonCovariantSeq(dogRefs) +reject modifiesCovariantOperArray(dogRefs) +reject wantsNonCovariantArray(dogRefsArray) +reject wantsNonCovariantSeq(dogValues) +reject wantsNonCovariantArray(dogValuesArray) +reject modifiesValueArray() + +modifiesDerivedOperArray dogRefs +reject modifiesDerivedOperArray(dogValues) +reject modifiesDerivedOperArray(animalRefs) + +wantsNonCovariantOperArray animalValues +reject wantsNonCovariantOperArray(animalRefs) +reject wantsNonCovariantOperArray(dogRefs) +reject wantsNonCovariantOperArray(dogValues) var animalRefSeq: seq[ref Animal] accept: - animalRefArray = [AnimalRef(dog), AnimalRef(dog)] - animalRefArray = [AnimalRef(cat), dog] + animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)] + animalRefSeq = @[AnimalRef(cat), dog] -reject: - animalRefArray = [dog, dog] +acceptWithCovariance: + animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] + wantsCovariantSeq1(animalRefSeq) +else: + echo "dog 1" + echo "dog 2" var pdog: ptr Dog var pcat: ptr Cat @@ -116,17 +298,6 @@ proc wantsVarPointer2(x: var AnimalPtr) = reject wantsVarPointer1(pdog) reject wantsVarPointer2(pcat) -proc usesVarRefSeq(x: var seq[AnimalRef]) = discard -proc usesVarRefArray(x: var array[2, AnimalRef]) = discard - -reject: - var catsSeq = @[cat, cat] - usesVarRefSeq catsSeq - -reject: - var catsArray = [cat, cat] - usesVarRefArray catsArray - # covariance may be allowed for certain extern types {.emit: """ @@ -178,9 +349,6 @@ var reject: ptrPtrDog = ptrPtrAnimal -type - RefAlias[T] = ref T - # Try to break the rules by introducing some tricky # double indirection types: var From 2106598d30f990db18c67d87ea08514cf21ffc1c Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 18:43:14 +0300 Subject: [PATCH 13/19] explain covariance in the manual --- doc/manual/type_rel.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt index 5b68f73aa..1a753d773 100644 --- a/doc/manual/type_rel.txt +++ b/doc/manual/type_rel.txt @@ -111,6 +111,20 @@ relation is extended to the types ``var``, ``ref``, ``ptr``: .. XXX nil is a special value! +Covariance +---------- + +Covariance in Nim can be introduced only though pointer-like types such +as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated +with pointer-like types will be considered covariant if and only if they +are also immutable. The introduction of a ``var`` modifier or addional +``ptr`` or ``ref`` indirections would result in invariant treatment of +these types. + +``proc`` types are curently always invariant, but future version of Nim +may relax this rule. + + Convertible relation -------------------- A type ``a`` is **implicitly** convertible to type ``b`` iff the following @@ -119,6 +133,8 @@ algorithm returns true: .. code-block:: nim # XXX range types? proc isImplicitlyConvertible(a, b: PType): bool = + if isSubtype(a, b) or isCovariant(a, b): + return true case a.kind of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float, float32, float64} From 7e0c66ffe79f1616c1c821ede5f50f03406feabe Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 21:27:32 +0300 Subject: [PATCH 14/19] manual additions for the covariant generic parameters --- doc/manual/type_rel.txt | 89 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt index 1a753d773..0c49f4e7f 100644 --- a/doc/manual/type_rel.txt +++ b/doc/manual/type_rel.txt @@ -117,13 +117,98 @@ Covariance Covariance in Nim can be introduced only though pointer-like types such as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated with pointer-like types will be considered covariant if and only if they -are also immutable. The introduction of a ``var`` modifier or addional +are also immutable. The introduction of a ``var`` modifier or additional ``ptr`` or ``ref`` indirections would result in invariant treatment of these types. -``proc`` types are curently always invariant, but future version of Nim +``proc`` types are currently always invariant, but future versions of Nim may relax this rule. +User-defined generic types may also be covariant with respect to some of +their parameters. By default, all generic params are considered invariant, +but you may choose the apply the prefix modifier ``in`` to a parameter to +make it contravariant or ``out`` to make it covariant: + +.. code-block:: nim + type + AnnotatedPtr[out T] = + metadata: MyTypeInfo + p: ref T + + RingBuffer[out T] = + startPos: int + data: seq[T] + + Action {.importcpp: "std::function".} [in T] = object + +When the designated generic parameter is used to instantiate a pointer-like +type as in the case of `AnnotatedPtr` above, the resulting generic type will +also have pointer-like covariance: + +.. code-block:: nim + type + GuiWidget = object of TObject + Button = object of GuiWidget + ComboBox = object of GuiWidget + + var + widgetPtr: AnnotatedPtr[GuiWidget] + buttonPtr: AnnotatedPtr[Button] + + ... + + proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ... + + # you can call procs expecting base types by supplying a derived type + drawWidget(buttonPtr) + + # and you can convert more-specific pointer types to more general ones + widgetPtr = buttonPtr + +Just like with regular pointers, covariance will be enabled only for immutable +values: + +.. code-block:: nim + proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) = + x.p = new(ComboBox) + + makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified + # to point to a ComboBox + +On the other hand, in the `RingBuffer` example above, the designated generic +param is used to instantiate the non-pointer ``seq`` type, which means that +the resulting generic type will have covariance that mimics an array or +sequence (i.e. it will be covariant only when instantiated with ``ptr`` and +``ref`` types): + +.. code-block:: nim + + type + Base = object of TObject + Derived = object of Base + + proc consumeBaseValues(b: RingBuffer[Base]) = ... + + var derivedValues: RingBuffer[Derived] + + consumeBaseValues(derivedValues) # Error, Base and Derived values may differ + # in size + + proc consumeBasePointers(b: RingBuffer[ptr Base]) = ... + + var derivedPointers: RingBuffer[ptr Derived] + + consumeBaseValues(derivedPointers) # This is legal + +Please note that Nim will treat the user-defined pointer-like types as +proper alternatives to the built-in pointer types. That is, types such +as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be +considered covariant and you can create new pointer-like types by instantiating +other user-defined pointer-like types. + +The contravariant parameters introduced with the ``in`` modifier are currently +useful only when interfacing with imported types having such semantics. + Convertible relation -------------------- From 0aede22e87926304ca1eea0e96805e4e58fb5d7b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 13 May 2017 14:38:07 +0300 Subject: [PATCH 15/19] enforce the covariance rules for user-defined generic types --- compiler/ast.nim | 14 ++++--- compiler/msgs.nim | 2 +- compiler/semstmts.nim | 88 ++++++++++++++++++++++++++++++++++++++++++- compiler/semtypes.nim | 10 ++--- compiler/sigmatch.nim | 8 ++-- 5 files changed, 105 insertions(+), 17 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 4bd76e41e..ee6391919 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -270,8 +270,8 @@ type sfDiscardable, # returned value may be discarded implicitly sfOverriden, # proc is overriden sfGenSym # symbol is 'gensym'ed; do not add to symbol table - sfCovariant # covariant generic param mimicing seq/array type - sfStrongCovariant # covariant generic param mimicing ptr type + sfCovariant # covariant generic param mimicing a ptr type + sfWeakCovariant # covariant generic param mimicing a seq/array type sfContravariant # contravariant generic param TSymFlags* = set[TSymFlag] @@ -1009,15 +1009,17 @@ proc add*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc `[]`*(n: PNode, i: int): PNode {.inline.} = - result = n.sons[i] +type Indexable = PNode | PType + +template `[]`*(n: Indexable, i: int): Indexable = + n.sons[i] template `-|`*(b, s: untyped): untyped = (if b >= 0: b else: s.len + b) # son access operators with support for negative indices -template `{}`*(n: PNode, i: int): untyped = n[i -| n] -template `{}=`*(n: PNode, i: int, s: PNode) = +template `{}`*(n: Indexable, i: int): untyped = n[i -| n] +template `{}=`*(n: Indexable, i: int, s: Indexable) = n.sons[i -| n] = s when defined(useNodeIds): diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1842ce1de..e416a3826 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -381,7 +381,7 @@ const "of the generic paramers can be inferred from the expected signature.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", - errInOutFlagNotExtern: "`in` and `out` flags can be used only with imported types", + errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e52a1b5cc..4c1fbede3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -730,7 +730,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = var s: PSym if name.kind == nkDotExpr: s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) - if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: + if s.kind != skType or + s.typ.skipTypes(abstractPtrs).kind != tyObject or + tfPartial notin s.typ.skipTypes(abstractPtrs).flags: localError(name.info, "only .partial objects can be extended") else: s = semIdentDef(c, name, skType) @@ -742,6 +744,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if sfGenSym notin s.flags: addInterfaceDecl(c, s) a.sons[0] = newSymNode(s) +proc checkCovariantParamsUsages(genericType: PType) = + var body = genericType{-1} + + proc traverseSubTypes(t: PType): bool = + template error(msg) = localError(genericType.sym.info, msg) + + result = false + + template subresult(r) = + let sub = r + result = result or sub + + case t.kind + of tyGenericParam: + t.sym.flags.incl sfWeakCovariant + return true + + of tyObject: + for field in t.n: + subresult traverseSubTypes(field.typ) + + of tyArray: + return traverseSubTypes(t[1]) + + of tyProc: + for subType in t.sons: + if subType != nil: + subresult traverseSubTypes(subType) + if result: + error("non-invariant type param used in a proc type: " & $t) + + of tySequence: + return traverseSubTypes(t[0]) + + of tyGenericInvocation: + let targetBody = t[0] + for i in 1 .. 0: - if t.sym != nil and sfCovariant in t.sym.flags: - t.sym.flags.incl sfStrongCovariant if t.kind == tyTypeDesc and tfUnresolved notin t.flags: t = t.base result = newOrPrevType(kind, prev, c) @@ -1591,10 +1588,13 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = var covarianceFlag = sfPure if paramName.kind in {nkInTy, nkOutTy}: - if father == nil or sfImportc notin father.sym.flags: - localError(paramName.info, errInOutFlagNotExtern) + if not nimEnableCovariance or paramName.kind == nkInTy: + if father == nil or sfImportc notin father.sym.flags: + localError(paramName.info, errInOutFlagNotExtern, + if paramName.kind == nkInTy: "in" else: "out") covarianceFlag = if paramName.kind == nkInTy: sfContravariant else: sfCovariant + if father != nil: father.sym.flags.incl sfCovariant paramName = paramName[0] var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c2a9b78bf..36975c3db 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -883,7 +883,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = let body = f.base return body == a.base and a.sonsLen == 3 and - sfStrongCovariant in body.sons[0].sym.flags and + sfWeakCovariant notin body.sons[0].sym.flags and baseTypesCheck(f.sons[1], a.sons[1]) else: return false @@ -1305,10 +1305,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let paramFlags = rootf.base.sons[i-1].sym.flags hasCovariance = if sfCovariant in paramFlags: - if sfStrongCovariant in paramFlags: - ff.kind notin {tyRef, tyPtr} and result == isSubtype - else: + if sfWeakCovariant in paramFlags: isCovariantPtr(c, ff, aa) + else: + ff.kind notin {tyRef, tyPtr} and result == isSubtype else: sfContravariant in paramFlags and typeRel(c, aa, ff) == isSubtype From b428da2cc2e7785ed9cb9a7efa1d2fa219603b59 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 13 May 2017 15:46:08 +0300 Subject: [PATCH 16/19] work-around for defining imported types with weak covariance --- compiler/semstmts.nim | 16 ++++++++++++++++ tests/typerel/tcovariancerules.nim | 11 +++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4c1fbede3..10fc6fb6d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -867,6 +867,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = s.typ.sons[sonsLen(s.typ) - 1] = body if sfCovariant in s.flags: checkCovariantParamsUsages(s.typ) + # XXX: This is a temporary limitation: + # The codegen currently produces various failures with + # generic imported types that have fields, but we need + # the fields specified in order to detect weak covariance. + # The proper solution is to teach the codegen how to handle + # such types, because this would offer various interesting + # possibilities such as instantiating C++ generic types with + # garbage collected Nim types. + if sfImportc in s.flags: + var body = s.typ.lastSon + if body.kind == tyObject: + # erases all declared fields + body.n.sons = nil + + debug s.typ + echo s.typ[0].sym.flags popOwner(c) closeScope(c) diff --git a/tests/typerel/tcovariancerules.nim b/tests/typerel/tcovariancerules.nim index 7cd5d5066..dfe4cb941 100644 --- a/tests/typerel/tcovariancerules.nim +++ b/tests/typerel/tcovariancerules.nim @@ -44,7 +44,7 @@ template acceptWithCovariance(x, otherwise): typed = skipElse(otherwise) type - Animal = object of TObject + Animal = object of RootObj x: string Dog = object of Animal @@ -302,12 +302,15 @@ reject wantsVarPointer2(pcat) {.emit: """ template struct FN { typedef void (*type)(T); }; -template struct ARR { typedef T type[2]; }; +template struct ARR { typedef T DataType[2]; DataType data; }; """.} type - MyPtr {.importcpp: "'0 *"} [out T] = distinct ptr T - MySeq {.importcpp: "ARR<'0>::type"} [out T] = object + MyPtr {.importcpp: "'0 *"} [out T] = object + + MySeq {.importcpp: "ARR<'0>", nodecl} [out T] = object + data: array[2, T] + MyAction {.importcpp: "FN<'0>::type"} [in T] = object var From cdb011afdcef5c9f634403cc121f1ae1d4dccafb Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 13 May 2017 15:46:47 +0300 Subject: [PATCH 17/19] attempt to fix a compilation problem caused by nimcache --- tests/{typerel => cpp}/tcovariancerules.nim | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{typerel => cpp}/tcovariancerules.nim (100%) diff --git a/tests/typerel/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim similarity index 100% rename from tests/typerel/tcovariancerules.nim rename to tests/cpp/tcovariancerules.nim From 05bfa4799697a4e06c4777b03f8009bebd4129d9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 13 May 2017 16:31:44 +0300 Subject: [PATCH 18/19] fix tinvalidinout --- compiler/semstmts.nim | 3 --- tests/errmsgs/tinvalidinout.nim | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 10fc6fb6d..f15cdddee 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -881,9 +881,6 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # erases all declared fields body.n.sons = nil - debug s.typ - echo s.typ[0].sym.flags - popOwner(c) closeScope(c) elif a.sons[2].kind != nkEmpty: diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim index 463c9951a..ce7eb6022 100644 --- a/tests/errmsgs/tinvalidinout.nim +++ b/tests/errmsgs/tinvalidinout.nim @@ -1,10 +1,10 @@ discard """ cmd: "nim check $file" -errormsg: "`in` and `out` flags can be used only with imported types" +errormsg: "The `in` modifier can be used only with imported types" nimout: ''' -tinvalidinout.nim(14, 7) Error: `in` and `out` flags can be used only with imported types -tinvalidinout.nim(17, 9) Error: `in` and `out` flags can be used only with imported types -tinvalidinout.nim(18, 9) Error: `in` and `out` flags can be used only with imported types +tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types +tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types +tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types ''' """ From 672c24e4b8fcfc07cdba6a36a2fc0445cdc3d9e9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 13 May 2017 20:48:33 +0300 Subject: [PATCH 19/19] fix affecting templates with explicit generic params --- compiler/semexprs.nim | 31 +++++----------------------- compiler/vm.nim | 5 +++-- doc/manual/type_rel.txt | 4 ++-- tests/template/tgenerictemplates.nim | 13 ++++++++++++ 4 files changed, 23 insertions(+), 30 deletions(-) create mode 100644 tests/template/tgenerictemplates.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4baef5385..a43591784 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -683,27 +683,9 @@ proc bracketedMacro(n: PNode): PSym = if result.kind notin {skMacro, skTemplate}: result = nil -proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; - flags: TExprFlags): PNode = - # We received untransformed bracket expression coming from macroOrTmpl[]. - # Transform it to macro or template call, where first come normal - # arguments, next come generic template arguments. - var sons = newSeq[PNode]() - sons.add inner.sons[0] - # Normal arguments: - for i in 1..