mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
Merge branch 'zahary' into araq2
This commit is contained in:
@@ -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,9 @@ 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 a ptr type
|
||||
sfWeakCovariant # covariant generic param mimicing a seq/array type
|
||||
sfContravariant # contravariant generic param
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
@@ -1004,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):
|
||||
|
||||
@@ -2203,7 +2203,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
|
||||
let field = obj.sym
|
||||
for i in 1..<cons.len:
|
||||
if cons[i].kind == nkExprColonExpr:
|
||||
if cons[i][0].sym == field:
|
||||
if cons[i][0].sym.name == field.name:
|
||||
result.add genConstExpr(p, cons[i][1])
|
||||
return
|
||||
elif i == field.position:
|
||||
|
||||
@@ -103,3 +103,4 @@ proc initDefines*() =
|
||||
defineSymbol("nimNewShiftOps")
|
||||
defineSymbol("nimDistros")
|
||||
defineSymbol("nimHasCppDefine")
|
||||
defineSymbol("nimGenericInOutFlags")
|
||||
|
||||
@@ -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: "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",
|
||||
|
||||
@@ -1520,6 +1520,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
|
||||
@@ -1548,7 +1555,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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..<outer.len:
|
||||
sons.add outer.sons[i]
|
||||
# Generic template arguments from bracket expression:
|
||||
for i in 1..<inner.len:
|
||||
sons.add inner.sons[i]
|
||||
shallowCopy(outer.sons, sons)
|
||||
# FIXME: Shouldn't we check sfImmediate and call semDirectOp?
|
||||
# However passing to semDirectOp doesn't work here.
|
||||
case s.kind
|
||||
of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, outer, s, flags)
|
||||
else: assert(false)
|
||||
return
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
for i in 1 .. <n.len:
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
|
||||
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||
result = n
|
||||
@@ -745,7 +727,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
elif n.sons[0].kind == nkBracketExpr:
|
||||
let s = bracketedMacro(n.sons[0])
|
||||
if s != nil:
|
||||
return semBracketedMacro(c, n, n.sons[0], s, flags)
|
||||
setGenericParams(c, n[0])
|
||||
return semDirectOp(c, n, flags)
|
||||
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -2164,10 +2147,6 @@ proc shouldBeBracketExpr(n: PNode): bool =
|
||||
n.sons[0] = be
|
||||
return true
|
||||
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
for i in 1 .. <n.len:
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result = n
|
||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 .. <t.len:
|
||||
let param = t[i]
|
||||
if param.kind == tyGenericParam:
|
||||
if sfCovariant in param.sym.flags:
|
||||
let formalFlags = targetBody[i-1].sym.flags
|
||||
if sfCovariant notin formalFlags:
|
||||
error("covariant param '" & param.sym.name.s &
|
||||
"' used in a non-covariant position")
|
||||
elif sfWeakCovariant in formalFlags:
|
||||
param.sym.flags.incl sfWeakCovariant
|
||||
result = true
|
||||
elif sfContravariant in param.sym.flags:
|
||||
let formalParam = targetBody[i-1].sym
|
||||
if sfContravariant notin formalParam.flags:
|
||||
error("contravariant param '" & param.sym.name.s &
|
||||
"' used in a non-contravariant position")
|
||||
result = true
|
||||
else:
|
||||
subresult traverseSubTypes(param)
|
||||
|
||||
of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass:
|
||||
error("non-invariant type parameters cannot be used with types such '" & $t & "'")
|
||||
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
error("non-invariant type parameters are not supported in concepts")
|
||||
|
||||
of tyTuple:
|
||||
for fieldType in t.sons:
|
||||
subresult traverseSubTypes(fieldType)
|
||||
|
||||
of tyPtr, tyRef, tyVar:
|
||||
if t.base.kind == tyGenericParam: return true
|
||||
return traverseSubTypes(t.base)
|
||||
|
||||
of tyDistinct, tyAlias:
|
||||
return traverseSubTypes(t.lastSon)
|
||||
|
||||
of tyGenericInst:
|
||||
internalAssert false
|
||||
|
||||
else:
|
||||
discard
|
||||
|
||||
discard traverseSubTypes(body)
|
||||
|
||||
proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
@@ -782,6 +865,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
body.sym = s
|
||||
body.size = -1 # could not be computed properly
|
||||
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
|
||||
|
||||
popOwner(c)
|
||||
closeScope(c)
|
||||
elif a.sons[2].kind != nkEmpty:
|
||||
|
||||
@@ -1584,10 +1584,25 @@ 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 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:
|
||||
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
|
||||
|
||||
@@ -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
|
||||
sfWeakCovariant notin 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
|
||||
@@ -956,23 +987,28 @@ 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):
|
||||
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
|
||||
|
||||
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)
|
||||
let x = typeRel(c, f, branch, flags + {trDontBind})
|
||||
if x != isNone:
|
||||
return if x >= isGeneric: isGeneric else: x
|
||||
result = isNone
|
||||
return isNone
|
||||
|
||||
of tyNot:
|
||||
case f.kind
|
||||
@@ -996,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
|
||||
@@ -1042,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
|
||||
@@ -1056,9 +1092,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
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)
|
||||
@@ -1077,20 +1121,31 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
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:
|
||||
@@ -1105,8 +1160,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
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
|
||||
@@ -1158,7 +1222,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:
|
||||
@@ -1230,15 +1294,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 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
|
||||
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}:
|
||||
@@ -1450,7 +1529,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
|
||||
|
||||
@@ -1609,8 +1609,9 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
|
||||
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
|
||||
let gp = macroSym.ast[genericParamsPos]
|
||||
for i in 0 .. <gp.len:
|
||||
let idx = macroSym.typ.len + i
|
||||
yield (gp[i].sym, call.sons[idx])
|
||||
let genericParam = gp[i].sym
|
||||
let posInCall = macroSym.typ.len + i
|
||||
yield (genericParam, call[posInCall])
|
||||
|
||||
var evalMacroCounter: int
|
||||
|
||||
|
||||
@@ -111,6 +111,105 @@ 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 additional
|
||||
``ptr`` or ``ref`` indirections would result in invariant treatment of
|
||||
these types.
|
||||
|
||||
``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<void ('0)>".} [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 RootObj
|
||||
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 RootObj
|
||||
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
|
||||
--------------------
|
||||
A type ``a`` is **implicitly** convertible to type ``b`` iff the following
|
||||
@@ -119,6 +218,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}
|
||||
|
||||
@@ -68,6 +68,8 @@ type
|
||||
nnkProcTy,
|
||||
nnkIteratorTy, # iterator type
|
||||
nnkSharedTy, # 'shared T'
|
||||
nnkInTy,
|
||||
nnkOutTy,
|
||||
nnkEnumTy,
|
||||
nnkEnumFieldDef,
|
||||
nnkArglist, nnkPattern
|
||||
|
||||
@@ -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,15 @@ const
|
||||
{.push warning[GcMem]: off, warning[Uninit]: off.}
|
||||
{.push hints: off.}
|
||||
|
||||
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 +78,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
|
||||
@@ -1332,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
|
||||
|
||||
424
tests/cpp/tcovariancerules.nim
Normal file
424
tests/cpp/tcovariancerules.nim
Normal file
@@ -0,0 +1,424 @@
|
||||
discard """
|
||||
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) =
|
||||
static: assert(compiles(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 RootObj
|
||||
x: string
|
||||
|
||||
Dog = object of Animal
|
||||
y: int
|
||||
|
||||
Cat = object of Animal
|
||||
z: int
|
||||
|
||||
AnimalRef = ref Animal
|
||||
AnimalPtr = ptr Animal
|
||||
|
||||
RefAlias[T] = ref T
|
||||
|
||||
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:
|
||||
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
|
||||
wantsCovariantArray([cat, dog])
|
||||
|
||||
acceptWithCovariance:
|
||||
wantsCovariantArray([cat, cat])
|
||||
else:
|
||||
echo "cat"
|
||||
echo "cat"
|
||||
|
||||
var animalRefArray: array[2, ref Animal]
|
||||
|
||||
accept:
|
||||
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
||||
animalRefArray = [AnimalRef(cat), dog]
|
||||
|
||||
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
|
||||
|
||||
# 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:
|
||||
wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)])
|
||||
wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)])
|
||||
wantsCovariantSeq1(@[AnimalRef(cat), dog])
|
||||
wantsCovariantSeq1(@[cat, dog])
|
||||
|
||||
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:
|
||||
animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)]
|
||||
animalRefSeq = @[AnimalRef(cat), 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
|
||||
|
||||
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)
|
||||
|
||||
# covariance may be allowed for certain extern types
|
||||
|
||||
{.emit: """
|
||||
template <class T> struct FN { typedef void (*type)(T); };
|
||||
template <class T> struct ARR { typedef T DataType[2]; DataType data; };
|
||||
""".}
|
||||
|
||||
type
|
||||
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
|
||||
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
|
||||
|
||||
# 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)
|
||||
|
||||
26
tests/errmsgs/tinvalidinout.nim
Normal file
26
tests/errmsgs/tinvalidinout.nim
Normal file
@@ -0,0 +1,26 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
errormsg: "The `in` modifier can be used only with imported types"
|
||||
nimout: '''
|
||||
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
|
||||
'''
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
78
tests/generics/tfakecovariance.nim
Normal file
78
tests/generics/tfakecovariance.nim
Normal file
@@ -0,0 +1,78 @@
|
||||
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
|
||||
|
||||
var base: BaseObj
|
||||
var derived: DerivedObj
|
||||
var nonDerived: NonDerivedObj
|
||||
|
||||
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 D
|
||||
var derived: ref D
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
39
tests/generics/tgenericconst.nim
Normal file
39
tests/generics/tgenericconst.nim
Normal file
@@ -0,0 +1,39 @@
|
||||
discard """
|
||||
output: '''
|
||||
@[1, 2]
|
||||
@[3, 4]
|
||||
1
|
||||
'''
|
||||
"""
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/5756
|
||||
|
||||
type
|
||||
Vec*[N : static[int]] = object
|
||||
x: int
|
||||
arr*: array[N, int32]
|
||||
|
||||
Mat*[M,N: static[int]] = object
|
||||
x: int
|
||||
arr*: array[M, Vec[N]]
|
||||
|
||||
proc vec2*(x,y:int32) : Vec[2] =
|
||||
result.arr = [x,y]
|
||||
result.x = 10
|
||||
|
||||
proc mat2*(a,b: Vec[2]): Mat[2,2] =
|
||||
result.arr = [a,b]
|
||||
result.x = 20
|
||||
|
||||
const M = mat2(vec2(1, 2), vec2(3, 4))
|
||||
|
||||
let m1 = M
|
||||
echo @(m1.arr[0].arr)
|
||||
echo @(m1.arr[1].arr)
|
||||
|
||||
proc foo =
|
||||
let m2 = M
|
||||
echo m1.arr[0].arr[0]
|
||||
|
||||
foo()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
13
tests/template/tgenerictemplates.nim
Normal file
13
tests/template/tgenerictemplates.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
type
|
||||
SomeObj = object of RootObj
|
||||
|
||||
Foo[T, U] = object
|
||||
x: T
|
||||
y: U
|
||||
|
||||
template someTemplate[T](): tuple[id: int32, obj: T] =
|
||||
var result: tuple[id: int32, obj: T] = (0'i32, T())
|
||||
result
|
||||
|
||||
let ret = someTemplate[SomeObj]()
|
||||
|
||||
@@ -33,13 +33,13 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user