Merge remote-tracking branch 'origin/concepts-rebased' into araq

This commit is contained in:
Andreas Rumpf
2017-03-31 16:40:21 +02:00
47 changed files with 2038 additions and 345 deletions

View File

@@ -252,6 +252,7 @@ type
sfProcvar, # proc can be passed to a proc var
sfDiscriminant, # field is a discriminant in a record/object
sfDeprecated, # symbol is deprecated
sfExplain, # provide more diagnostics when this symbol is used
sfError, # usage of symbol should trigger a compile-time error
sfShadowed, # a symbol that was shadowed in some inner scope
sfThread, # proc will run as a thread
@@ -354,44 +355,52 @@ type
tyUnused,
tyProxy # used as errornous type (for idetools)
tyBuiltInTypeClass #\
tyBuiltInTypeClass
# Type such as the catch-all object, tuple, seq, etc
tyUserTypeClass #\
tyUserTypeClass
# the body of a user-defined type class
tyUserTypeClassInst #\
tyUserTypeClassInst
# Instance of a parametric user-defined type class.
# Structured similarly to tyGenericInst.
# tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation.
tyCompositeTypeClass #\
tyCompositeTypeClass
# Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
# sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
tyAnd, tyOr, tyNot #\
tyInferred
# In the initial state `base` stores a type class constraining
# the types that can be inferred. After a candidate type is
# selected, it's stored in `lastSon`. Between `base` and `lastSon`
# there may be 0, 2 or more types that were also considered as
# possible candidates in the inference process (i.e. lastSon will
# be updated to store a type best conforming to all candidates)
tyAnd, tyOr, tyNot
# boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc
tyAnything #\
tyAnything
# a type class matching any type
tyStatic #\
tyStatic
# a value known at compile type (the underlying type is .base)
tyFromExpr #\
tyFromExpr
# This is a type representing an expression that depends
# on generic parameters (the expression is stored in t.n)
# It will be converted to a real type only during generic
# instantiation and prior to this it has the potential to
# be any type.
tyFieldAccessor #\
tyFieldAccessor
# Expressions such as Type.field (valid in contexts such
# as the `is` operator and magics like `high` and `low`).
# Could be lifted to a single argument proc returning the
@@ -400,7 +409,7 @@ type
# sons[1]: field type
# .n: nkDotExpr storing the field name
tyVoid #\
tyVoid
# now different from tyEmpty, hurray!
static:
@@ -420,6 +429,7 @@ const
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
tyUserTypeClasses* = {tyUserTypeClass, tyUserTypeClassInst}
type
TTypeKinds* = set[TTypeKind]
@@ -463,6 +473,8 @@ type
# can be attached to generic procs with free standing
# type parameters: e.g. proc foo[T]()
# depends on unresolved static params.
tfResolved # marks a user type class, after it has been bound to a
# concrete type (lastSon becomes the concrete type)
tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference)
tfCapturesEnv, # whether proc really captures some environment
@@ -482,6 +494,9 @@ type
tfHasStatic
tfGenericTypeParam
tfImplicitTypeParam
tfInferrableStatic
tfExplicit # for typedescs, marks types explicitly prefixed with the
# `type` operator (e.g. type int)
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
# T and I here can bind to both typedesc and static types
# before this is determined, we'll consider them to be a
@@ -1036,6 +1051,9 @@ proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind)
result.strVal = strVal
template previouslyInferred*(t: PType): PType =
if t.sons.len > 1: t.lastSon else: nil
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too
@@ -1062,6 +1080,9 @@ proc isMetaType*(t: PType): bool =
(t.kind == tyStatic and t.n == nil) or
tfHasMeta in t.flags
proc isUnresolvedStatic*(t: PType): bool =
return t.kind == tyStatic and t.n == nil
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
t.sym = s
s.typ = t
@@ -1278,6 +1299,8 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
when debugIds: registerId(result)
result.sym = t.sym # backend-info should not be copied
proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)
proc copySym*(s: PSym, keepId: bool = false): PSym =
result = newSym(s.kind, s.name, s.owner, s.info)
#result.ast = nil # BUGFIX; was: s.ast which made problems
@@ -1561,7 +1584,7 @@ proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
iterator items*(n: PNode): PNode =
for i in 0.. <n.len: yield n.sons[i]
for i in 0.. <n.safeLen: yield n.sons[i]
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
for i in 0.. <n.len: yield (i, n.sons[i])
@@ -1604,6 +1627,16 @@ proc toObject*(typ: PType): PType =
if result.kind == tyRef:
result = result.lastSon
proc findUnresolvedStatic*(n: PNode): PNode =
if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
return n
for son in n:
let n = son.findUnresolvedStatic
if n != nil: return n
return nil
when false:
proc containsNil*(n: PNode): bool =
# only for debugging

View File

@@ -890,7 +890,7 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n.sons[0].typ, abstractVarRange)
var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind
of tyArray: genArrayElem(p, n.sons[0], n.sons[1], d)
@@ -1359,7 +1359,7 @@ proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var a = e.sons[1]
if a.kind == nkHiddenAddr: a = a.sons[0]
let typ = skipTypes(a.typ, abstractVar)
var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
case typ.kind
of tyOpenArray, tyVarargs:
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")

View File

@@ -164,8 +164,11 @@ proc mapType(typ: PType): TCTypeKind =
of tySet: result = mapSetType(typ)
of tyOpenArray, tyArray, tyVarargs: result = ctArray
of tyObject, tyTuple: result = ctStruct
of tyUserTypeClass, tyUserTypeClassInst:
internalAssert typ.isResolvedUserTypeClass
return mapType(typ.lastSon)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias:
tyTypeDesc, tyAlias, tyInferred:
result = mapType(lastSon(typ))
of tyEnum:
if firstOrd(typ) < 0:
@@ -787,7 +790,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
[result, rope(getSize(t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias:
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
tyUserTypeClass, tyUserTypeClassInst, tyInferred:
result = getTypeDescAux(m, lastSon(t), check)
else:
internalError("getTypeDescAux(" & $t.kind & ')')

View File

@@ -101,6 +101,8 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key
result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
if key.isResolvedUserTypeClass:
return getUniqueType(lastSon(key))
if key.sym != nil:
internalError(key.sym.info, "metatype not eliminated")
else:
@@ -108,7 +110,7 @@ proc getUniqueType*(key: PType): PType =
of tyDistinct:
if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
of tyGenericInst, tyOrdinal, tyStatic, tyAlias:
of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tyInferred:
result = getUniqueType(lastSon(key))
#let obj = lastSon(key)
#if obj.sym != nil and obj.sym.name.s == "TOption":

View File

@@ -478,8 +478,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "reportconceptfailures":
processOnOffSwitchG({optReportConceptFailures}, arg, pass, info)
of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info)
#if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)

View File

@@ -167,6 +167,8 @@ proc mapType(typ: PType): TJSTypeKind =
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias:
result = etyNone
of tyInferred:
result = mapType(typ.lastSon)
of tyStatic:
if t.n != nil: result = mapType(lastSon t)
else: result = etyNone

View File

@@ -109,6 +109,7 @@ type
errXCannotBeClosure, errXMustBeCompileTime,
errCannotInferTypeOfTheLiteral,
errCannotInferReturnType,
errCannotInferStaticParam,
errGenericLambdaNotAllowed,
errProcHasNoConcreteType,
errCompilerDoesntSupportTarget,
@@ -132,7 +133,7 @@ type
hintConditionAlwaysTrue, hintName, hintPattern,
hintExecuting, hintLinking, hintDependency,
hintSource, hintStackTrace, hintGCStats,
hintUser
hintUser, hintUserRaw
const
MsgKindToStr*: array[TMsgKind, string] = [
@@ -373,6 +374,7 @@ const
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errCannotInferReturnType: "cannot infer the return type of the proc",
errCannotInferStaticParam: "cannot infer the value of the static param `$1`",
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
"it is used as an operand to another routine and the types " &
"of the generic paramers can be inferred from the expected signature.",
@@ -432,10 +434,11 @@ const
hintSource: "$1",
hintStackTrace: "$1",
hintGCStats: "$1",
hintUser: "$1"]
hintUser: "$1",
hintUserRaw: "$1"]
const
WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
@@ -447,12 +450,12 @@ const
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
HintsToStr*: array[0..22, string] = ["Success", "SuccessX", "LineTooLong",
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "StackTrace", "GCStats",
"User"]
"User", "UserRaw"]
const
fatalMin* = errUnknown
@@ -497,7 +500,6 @@ type
TErrorOutput* = enum
eStdOut
eStdErr
eInMemory
TErrorOutputs* = set[TErrorOutput]
@@ -651,6 +653,12 @@ var
writelnHook*: proc (output: string) {.closure.}
structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.}
proc concat(strings: openarray[string]): string =
var totalLen = 0
for s in strings: totalLen += s.len
result = newStringOfCap totalLen
for s in strings: result.add s
proc suggestWriteln*(s: string) =
if eStdOut in errorOutputs:
if isNil(writelnHook):
@@ -804,10 +812,7 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
result.add(arg)
template callWritelnHook(args: varargs[string, `$`]) =
var s = ""
for arg in args:
s.add arg
writelnHook s
writelnHook concat(args)
template styledMsgWriteln*(args: varargs[typed]) =
if not isNil(writelnHook):
@@ -922,7 +927,7 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
if msg notin gNotes: return
title = HintTitle
color = HintColor
kind = HintsToStr[ord(msg) - ord(hintMin)]
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter)
let s = msgKindToString(msg) % args
@@ -990,7 +995,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
ignoreMsg = optHints notin gOptions or msg notin gNotes
title = HintTitle
color = HintColor
kind = HintsToStr[ord(msg) - ord(hintMin)]
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(gHintCounter)
# NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect

View File

@@ -41,7 +41,6 @@ type # please make sure we have under 32 options
TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking,
optReportConceptFailures, # report 'compiles' or 'concept' matching failures
optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library
optGenStaticLib, # generate a static library
@@ -73,8 +72,8 @@ type # please make sure we have under 32 options
TGlobalOptions* = set[TGlobalOption]
const
harmlessOptions* = {optForceFullMake, optNoLinking, optReportConceptFailures,
optRun, optUseColors, optStdout}
harmlessOptions* = {optForceFullMake, optNoLinking, optRun,
optUseColors, optStdout}
type
TCommands* = enum # Nim's commands

View File

@@ -235,6 +235,13 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
of nkStmtList, nkStmtListExpr:
if n.typ != nil:
result = isAssignable(owner, n.lastSon, isUnsafeAddr)
of nkVarTy:
# XXX: The fact that this is here is a bit of a hack.
# The goal is to allow the use of checks such as "foo(var T)"
# within concepts. Semantically, it's not correct to say that
# nkVarTy denotes an lvalue, but the example above is the only
# possible code which will get us here
result = arLValue
else:
discard

View File

@@ -66,6 +66,7 @@ proc parseSymbol*(p: var TParser, allowNil = false): PNode
proc parseTry(p: var TParser; isExpr: bool): PNode
proc parseCase(p: var TParser): PNode
proc parseStmtPragma(p: var TParser): PNode
proc parsePragma(p: var TParser): PNode
# implementation
proc getTok(p: var TParser) =
@@ -770,6 +771,13 @@ proc parseOperators(p: var TParser, headNode: PNode,
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
result = primary(p, mode)
if p.tok.tokType == tkCurlyDotLe and
p.lex.lineNumber == result.info.line and
mode == pmNormal:
var pragmaExp = newNodeP(nkPragmaExpr, p)
pragmaExp.addSon result
pragmaExp.addSon p.parsePragma
result = pragmaExp
result = parseOperators(p, result, limit, mode)
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
@@ -1793,8 +1801,16 @@ proc parseObject(p: var TParser): PNode =
addSon(result, parseObjectPart(p))
proc parseTypeClassParam(p: var TParser): PNode =
if p.tok.tokType in {tkOut, tkVar}:
result = newNodeP(nkVarTy, p)
let modifier = case p.tok.tokType
of tkOut, tkVar: nkVarTy
of tkPtr: nkPtrTy
of tkRef: nkRefTy
of tkStatic: nkStaticTy
of tkType: nkTypeOfExpr
else: nkEmpty
if modifier != nkEmpty:
result = newNodeP(modifier, p)
getTok(p)
result.addSon(p.parseSymbol)
else:

View File

@@ -55,7 +55,7 @@ const
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wExportNims, wPartial, wUsed}
wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@@ -73,7 +73,7 @@ const
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
# implementation
proc invalidPragma(n: PNode) =
proc invalidPragma*(n: PNode) =
localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
proc pragmaAsm*(c: PContext, n: PNode): char =
@@ -773,6 +773,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wProcVar:
noVal(it)
incl(sym.flags, sfProcvar)
of wExplain:
sym.flags.incl sfExplain
of wDeprecated:
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated)

View File

@@ -34,6 +34,7 @@ type
pendingWhitespace: int
comStack*: seq[PNode] # comment stack
flags*: TRenderFlags
inGenericParams: bool
checkAnon: bool # we're in a context that can contain sfAnon
inPragma: int
@@ -83,7 +84,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
g.flags = renderFlags
g.pendingNL = -1
g.pendingWhitespace = -1
g.checkAnon = false
g.inGenericParams = false
proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
var length = len(g.tokens)
@@ -692,14 +693,14 @@ proc gproc(g: var TSrcGen, n: PNode) =
if n.sons[patternPos].kind != nkEmpty:
gpattern(g, n.sons[patternPos])
let oldCheckAnon = g.checkAnon
g.checkAnon = true
let oldInGenericParams = g.inGenericParams
g.inGenericParams = true
if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
n[miscPos][1].kind != nkEmpty:
gsub(g, n[miscPos][1])
else:
gsub(g, n.sons[genericParamsPos])
g.checkAnon = oldCheckAnon
g.inGenericParams = oldInGenericParams
gsub(g, n.sons[paramsPos])
gsub(g, n.sons[pragmasPos])
if renderNoBody notin g.flags:
@@ -765,7 +766,10 @@ proc gasm(g: var TSrcGen, n: PNode) =
gsub(g, n.sons[1])
proc gident(g: var TSrcGen, n: PNode) =
if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
if g.inGenericParams and n.kind == nkSym:
if sfAnon in n.sym.flags or
(n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
var t: TTokType
var s = atom(n)
if (s[0] in lexer.SymChars):
@@ -1315,9 +1319,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gcoms(g)
gstmts(g, lastSon(n), c)
of nkGenericParams:
put(g, tkBracketLe, "[")
gcomma(g, n)
put(g, tkBracketRi, "]")
proc hasExplicitParams(gp: PNode): bool =
for p in gp:
if p.typ == nil or tfImplicitTypeParam notin p.typ.flags:
return true
return false
if n.hasExplicitParams:
put(g, tkBracketLe, "[")
gcomma(g, n)
put(g, tkBracketRi, "]")
of nkFormalParams:
put(g, tkParLe, "(")
gsemicolon(g, n, 1)

View File

@@ -215,7 +215,6 @@ proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
proc isOpImpl(c: PContext, n: PNode): PNode
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,

View File

@@ -226,7 +226,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange,
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar, tyAlias:
liftBodyAux(c, lastSon(t), body, x, y)
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("liftBodyAux")

View File

@@ -35,10 +35,11 @@ proc sameMethodDispatcher(a, b: PSym): bool =
proc determineType(c: PContext, s: PSym)
proc initCandidateSymbols(c: PContext, headSymbol: PNode,
initialBinding: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
o: var TOverloadIter): seq[tuple[s: PSym, scope: int]] =
initialBinding: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
o: var TOverloadIter,
diagnostics: bool): seq[tuple[s: PSym, scope: int]] =
result = @[]
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
@@ -46,8 +47,10 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
result.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if result.len > 0:
initCandidate(c, best, result[0].s, initialBinding, result[0].scope)
initCandidate(c, alt, result[0].s, initialBinding, result[0].scope)
initCandidate(c, best, result[0].s, initialBinding,
result[0].scope, diagnostics)
initCandidate(c, alt, result[0].s, initialBinding,
result[0].scope, diagnostics)
best.state = csNoMatch
proc pickBestCandidate(c: PContext, headSymbol: PNode,
@@ -55,7 +58,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
initialBinding: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
errors: var CandidateErrors) =
errors: var CandidateErrors,
diagnosticsFlag = false) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope
@@ -68,8 +72,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
while sym != nil:
if sym.kind in filter:
# Initialise 'best' and 'alt' with the first available symbol
initCandidate(c, best, sym, initialBinding, scope)
initCandidate(c, alt, sym, initialBinding, scope)
initCandidate(c, best, sym, initialBinding, scope, diagnosticsFlag)
initCandidate(c, alt, sym, initialBinding, scope, diagnosticsFlag)
best.state = csNoMatch
break
else:
@@ -82,14 +86,9 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
scope = o.lastOverloadScope
continue
determineType(c, sym)
initCandidate(c, z, sym, initialBinding, scope)
initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
if c.currentScope.symbols.counter == counterInitial or syms != nil:
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd((sym, int z.mutabilityProblem))
if z.errors != nil:
for err in z.errors:
errors.add(err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200)
@@ -99,10 +98,16 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
elif errors != nil or z.diagnostics != nil:
errors.safeAdd(CandidateError(
sym: sym,
unmatchedVarParam: int z.mutabilityProblem,
diagnostics: z.diagnostics))
else:
# Symbol table has been modified. Restart and pre-calculate all syms
# before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o)
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag)
if syms == nil:
sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope
@@ -114,17 +119,9 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
else:
break
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error).
if c.compilesContextId > 0 and optReportConceptFailures notin gGlobalOptions:
# fail fast:
globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
(TPreferedDesc, string) =
var prefer = preferName
# to avoid confusing errors like:
# got (SslPtr, SocketHandle)
# but expected one of:
@@ -132,11 +129,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# we do a pre-analysis. If all types produce the same string, we will add
# module information.
let proto = describeArgs(c, n, 1, preferName)
var prefer = preferName
for err, mut in items(errors):
for err in errors:
var errProto = ""
let n = err.typ.n
let n = err.sym.typ.n
for i in countup(1, n.len - 1):
var p = n.sons[i]
if p.kind == nkSym:
@@ -147,26 +142,40 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
prefer = preferModuleInfo
break
# now use the information stored in 'prefer' to produce a nice error message:
var candidates = ""
for err in errors:
if err.sym.kind in routineKinds and err.sym.ast != nil:
add(candidates, renderTree(err.sym.ast,
{renderNoBody, renderNoComments, renderNoPragmas}))
else:
add(candidates, err.sym.getProcHeader(prefer))
add(candidates, "\n")
if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" &
renderTree(n[err.unmatchedVarParam]) & "' is immutable\n")
for diag in err.diagnostics:
add(candidates, diag & "\n")
result = (prefer, candidates)
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error).
if errorOutputs == {}:
# fail fast:
globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n, 1, prefer))
add(result, ')')
var candidates = ""
for err, mut in items(errors):
if err.kind in routineKinds and err.ast != nil:
add(candidates, renderTree(err.ast,
{renderNoBody, renderNoComments,renderNoPragmas}))
else:
add(candidates, err.getProcHeader(prefer))
add(candidates, "\n")
if mut != 0 and mut < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions:
globalError(n.info, errGenerated, result)
else:
localError(n.info, errGenerated, result)
localError(n.info, errGenerated, result)
proc bracketNotFoundError(c: PContext; n: PNode) =
var errors: CandidateErrors = @[]
@@ -175,7 +184,9 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
errors.add((symx, 0))
errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0,
diagnostics: nil))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(n.info, "could not resolve: " & $n)
@@ -183,7 +194,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
notFoundError(c, n, errors)
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds;
filter: TSymKinds, flags: TExprFlags,
errors: var CandidateErrors): TCandidate =
var initialBinding: PNode
var alt: TCandidate
@@ -197,7 +208,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors)
filter, result, alt, errors, efExplain in flags)
pickBest(f)
let overloadsState = result.state
@@ -263,18 +274,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
# clean up the inserted ops
n.sons.delete(2)
n.sons[0] = f
errors = @[]
pickBest(f)
#notFoundError(c, n, errors)
return
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
internalAssert result.state == csMatch
#writeMatches(result)
#writeMatches(alt)
if c.compilesContextId > 0:
if errorOutputs == {}:
# quick error message for performance of 'compiles' built-in:
globalError(n.info, errGenerated, "ambiguous call")
elif gErrorCounter == 0:
@@ -289,7 +295,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
args])
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym:
let s = a.sons[0].sym
@@ -378,23 +383,40 @@ proc tryDeref(n: PNode): PNode =
result.addSon(n)
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode =
var errors: CandidateErrors
var r = resolveOverloads(c, n, nOrig, filter, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r)
filter: TSymKinds, flags: TExprFlags): PNode =
var errors: CandidateErrors = if efExplain in flags: @[]
else: nil
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
if r.state == csMatch:
# this may be triggered, when the explain pragma is used
if errors.len > 0:
let (_, candidates) = presentFailedCandidates(c, n, errors)
message(n.info, hintUserRaw,
"Non-matching candidates for " & renderTree(n) & "\n" &
candidates)
result = semResolvedCall(c, n, r)
elif experimentalMode(c) and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
#
# XXX: why is this here?
# it could be added to the long list of alternatives tried
# inside `resolveOverloads` or it could be moved all the way
# into sigmatch with hidden conversion produced there
#
n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, errors)
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r)
else:
# get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors)
else:
notFoundError(c, n, errors)
# else: result = errorNode(c, n)
if efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
else:
notFoundError(c, n, errors)
proc explicitGenericInstError(n: PNode): PNode =
localError(n.info, errCannotInstantiateX, renderTree(n))
@@ -450,6 +472,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
if result.len == 1 and a.kind == nkClosedSymChoice:
result = result[0]
elif result.len == 0: result = explicitGenericInstError(n)
# candidateCount != 1: return explicitGenericInstError(n)
else:
result = explicitGenericInstError(n)
@@ -473,7 +496,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
x = t.baseOfDistinct
call.add(newNodeIT(nkEmpty, fn.info, x))
if hasDistinct:
var resolved = semOverloadedCall(c, call, call, {fn.kind})
var resolved = semOverloadedCall(c, call, call, {fn.kind}, {})
if resolved != nil:
result = resolved.sons[0].sym
if not compareTypes(result.typ.sons[0], fn.typ.sons[0], dcEqIgnoreDistinct):
@@ -481,4 +504,3 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
elif result.magic in {mArrPut, mArrGet}:
# cannot borrow these magics for now
result = nil

View File

@@ -46,9 +46,10 @@ type
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof,
efWantStmt, efAllowStmt, efDetermineType,
efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
@@ -84,12 +85,12 @@ type
libs*: seq[PLib] # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.}
filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
@@ -230,6 +231,17 @@ proc makePtrType*(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeTypeWithModifier*(c: PContext,
modifier: TTypeKind,
baseType: PType): PType =
assert modifier in {tyVar, tyPtr, tyRef, tyStatic, tyTypeDesc}
if modifier in {tyVar, tyTypeDesc} and baseType.kind == modifier:
result = baseType
else:
result = newTypeS(modifier, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType*(c: PContext, baseType: PType): PType =
if baseType.kind == tyVar:
result = baseType
@@ -238,8 +250,11 @@ proc makeVarType*(c: PContext, baseType: PType): PType =
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeTypeDesc*(c: PContext, typ: PType): PType =
result = newTypeS(tyTypeDesc, c)
result.addSonSkipIntLit(typ.assertNotNil)
if typ.kind == tyTypeDesc:
result = typ
else:
result = newTypeS(tyTypeDesc, c)
result.addSonSkipIntLit(typ.assertNotNil)
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
@@ -259,7 +274,8 @@ proc newTypeWithSons*(c: PContext, kind: TTypeKind,
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
result = newNodeI(nkStaticExpr, n.info)
result.sons = @[n]
result.typ = newTypeWithSons(c, tyStatic, @[n.typ])
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
else: newTypeWithSons(c, tyStatic, @[n.typ])
proc makeAndType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyAnd, c)
@@ -303,16 +319,14 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
let intType = getSysType(tyInt)
result = newTypeS(tyRange, c)
result.sons = @[intType]
if n.typ != nil and n.typ.n == nil:
result.flags.incl tfUnresolved
result.n = newNode(nkRange, n.info, @[
newIntTypeNode(nkIntLit, 0, intType),
makeStaticExpr(c, n.nMinusOne)])
template rangeHasStaticIf*(t: PType): bool =
# this accepts the ranges's node
t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
template getStaticTypeFromRange*(t: PType): PType =
t.n[1][0][1].typ
template rangeHasUnresolvedStatic*(t: PType): bool =
tfUnresolved in t.flags
proc errorType*(c: PContext): PType =
## creates a type representing an error state

View File

@@ -136,6 +136,7 @@ proc isCastable(dst, src: PType): bool =
# castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString,
# tySequence, tyPointer, tyNil, tyOpenArray,
# tyProc, tySet, tyEnum, tyBool, tyChar}
let src = src.skipTypes(tyUserTypeClasses)
if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray:
return false
if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc:
@@ -301,7 +302,7 @@ proc semOf(c: PContext, n: PNode): PNode =
n.typ = getSysType(tyBool)
result = n
proc isOpImpl(c: PContext, n: PNode): PNode =
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
internalAssert n.sonsLen == 3 and
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
@@ -318,16 +319,18 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
else:
result = newIntNode(nkIntLit, 0)
else:
var t2 = n[2].typ.skipTypes({tyTypeDesc})
var rhsOrigType = n[2].typ
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
maybeLiftType(t2, c, n.info)
var m: TCandidate
initCandidate(c, m, t2)
if efExplain in flags: m.diagnostics = @[]
let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc semIs(c: PContext, n: PNode): PNode =
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) != 3:
localError(n.info, errXExpectsTwoArguments, "is")
@@ -347,7 +350,7 @@ proc semIs(c: PContext, n: PNode): PNode =
return
# BUGFIX: don't evaluate this too early: ``T is void``
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n)
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType}
@@ -625,6 +628,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
proc semStaticExpr(c: PContext, n: PNode): PNode =
let a = semExpr(c, n.sons[0])
if a.findUnresolvedStatic != nil: return a
result = evalStaticExpr(c.module, c.cache, a, c.p.owner)
if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(n))
@@ -640,10 +644,10 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
# for typeof support.
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
else:
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate})
{skProc, skMethod, skConverter, skMacro, skTemplate}, flags)
if result != nil:
if result.sons[0].kind != nkSym:
@@ -751,7 +755,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# This is a proc variable, apply normal overload resolution
let m = resolveIndirectCall(c, n, nOrig, t)
if m.state != csMatch:
if c.compilesContextId > 0:
if errorOutputs == {}:
# speed up error generation:
globalError(n.info, errTypeMismatch, "")
return emptyNode
@@ -912,20 +916,42 @@ const
proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode =
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
else: (internalAssert(typ.kind == tyCompositeTypeClass);
typ.sons[1].skipGenericAlias)
let tbody = ty.sons[0]
for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s]
if tParam.sym.name.id == paramName.id:
let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic:
return rawTyp.n
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}:
for statement in typ.n:
case statement.kind
of nkTypeSection:
for def in statement:
if def[0].sym.name.id == paramName.id:
# XXX: Instead of lifting the section type to a typedesc
# here, we could try doing it earlier in semTypeSection.
# This seems semantically correct and then we'll be able
# to return the section symbol directly here
let foundType = makeTypeDesc(c, def[2].typ)
return newSymNode(copySym(def[0].sym).linkTo(foundType), info)
of nkConstSection:
for def in statement:
if def[0].sym.name.id == paramName.id:
return def[2]
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
#echo "came here: returned nil"
discard
if typ.kind != tyUserTypeClass:
let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias
else: typ.skipGenericAlias
let tbody = ty.sons[0]
for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s]
if tParam.sym.name.id == paramName.id:
let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic:
return rawTyp.n
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
return nil
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
let s = getGenSym(c, sym)
@@ -1080,6 +1106,23 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ
var f: PSym = nil
result = nil
template tryReadingGenericParam(t: PType) =
case t.kind
of tyTypeParamsHolders:
return readTypeParameter(c, t, i, n.info)
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
return readTypeParameter(c, t, i, n.info)
else:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
of tyGenericParam:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
else:
discard
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
if ty.kind == tyTypeDesc: ty = ty.base
ty = ty.skipTypes(tyDotOpTransparent)
@@ -1097,8 +1140,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
markUsed(n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
return
of tyTypeParamsHolders:
return readTypeParameter(c, ty, i, n.info)
of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList:
let field = lookupInRecord(ty.n, i)
@@ -1107,8 +1148,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ.n = copyTree(n)
return n
else:
# echo "TYPE FIELD ACCESS"
# debug ty
tryReadingGenericParam(ty)
return
# XXX: This is probably not relevant any more
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
@@ -1151,8 +1191,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we didn't find any field, let's look for a generic param
if result == nil:
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
if t.kind in tyTypeParamsHolders:
result = readTypeParameter(c, t, i, n.info)
tryReadingGenericParam(t)
proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n.sons[1]):
@@ -1718,7 +1757,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
let oldOwnerLen = len(c.graph.owners)
let oldGenerics = c.generics
let oldErrorOutputs = errorOutputs
errorOutputs = {}
if efExplain notin flags: errorOutputs = {}
let oldContextLen = msgs.getInfoContextLen()
let oldInGenericContext = c.inGenericContext
@@ -1731,8 +1770,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags)
if msgs.gErrorCounter != oldErrorCount: result = nil
except ERecoverableError:
if optReportConceptFailures in gGlobalOptions:
err = getCurrentExceptionMsg()
discard
# undo symbol table changes (as far as it's possible):
c.compilesContextId = oldCompilesId
c.generics = oldGenerics
@@ -1746,8 +1784,6 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
errorOutputs = oldErrorOutputs
msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax
if optReportConceptFailures in gGlobalOptions and not err.isNil:
localError(n.info, err)
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we replace this node by a 'true' or 'false' node:
@@ -1814,7 +1850,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mLow: result = semLowHigh(c, setMs(n, s), mLow)
of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
of mSizeOf: result = semSizeof(c, setMs(n, s))
of mIs: result = semIs(c, setMs(n, s))
of mIs: result = semIs(c, setMs(n, s), flags)
of mOf: result = semOf(c, setMs(n, s))
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, flags)
@@ -2327,8 +2363,20 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCurlyExpr:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
of nkPragmaExpr:
# which pragmas are allowed for expressions? `likely`, `unlikely`
internalError(n.info, "semExpr() to implement") # XXX: to implement
var
expr = n[0]
pragma = n[1]
pragmaName = considerQuotedIdent(pragma[0])
flags = flags
case whichKeyword(pragmaName)
of wExplain:
flags.incl efExplain
else:
# what other pragmas are allowed for expressions? `likely`, `unlikely`
invalidPragma(n)
result = semExpr(c, n[0], flags)
of nkPar:
case checkPar(n)
of paNone: result = errorNode(c, n)

View File

@@ -304,7 +304,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
if isNil(n.sons[bodyPos]):
n.sons[bodyPos] = copyTree(fn.getBody)
instantiateBody(c, n, fn.typ.n, result, fn)
if c.inGenericContext == 0:
instantiateBody(c, n, fn.typ.n, result, fn)
sideEffectsCheck(c, result)
paramsTypeCheck(c, result.typ)
else:

View File

@@ -24,7 +24,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
result = newNodeI(nkTypeOfExpr, n.info)
let typExpr = semExprWithType(c, n, {efInTypeof})
result.add typExpr
result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc}))
result.typ = makeTypeDesc(c, typExpr.typ)
type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
@@ -86,9 +86,32 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
result.add(filename)
result.add(line)
proc toNode(t: PType, i: TLineInfo): PNode =
result = newNodeIT(nkType, i, t)
const
# these are types that use the bracket syntax for instantiation
# they can be subjected to the type traits `genericHead` and
# `Uninstantiated`
tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation,
tyUserTypeClassInst}
tyMagicGenerics* = {tySet, tySequence, tyArray, tyOpenArray}
tyGenericLike* = tyUserDefinedGenerics +
tyMagicGenerics +
{tyCompositeTypeClass}
proc uninstantiate(t: PType): PType =
result = case t.kind
of tyMagicGenerics: t
of tyUserDefinedGenerics: t.base
of tyCompositeTypeClass: uninstantiate t.sons[1]
else: t
proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
let typ = operand.skipTypes({tyTypeDesc})
case trait.sym.name.s.normalize
var typ = operand.skipTypes({tyTypeDesc})
case trait.sym.name.s
of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferName))
result.typ = newType(tyString, context)
@@ -97,6 +120,16 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
result.typ = newType(tyInt, context)
result.info = trait.info
of "genericHead":
var res = uninstantiate(typ)
if res == typ and res.kind notin tyMagicGenerics:
localError(trait.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)
of "stripGenericParams":
result = uninstantiate(typ).toNode(trait.info)
else:
internalAssert false

View File

@@ -501,6 +501,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
#changeType(def.skipConv, typ, check=true)
else:
typ = skipIntLit(def.typ)
if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
typ = typ.lastSon
if hasEmpty(typ):
localError(def.info, errCannotInferTypeOfTheLiteral,
($typ.kind).substr(2).toLowerAscii)
@@ -1554,6 +1556,16 @@ proc usesResult(n: PNode): bool =
for c in n:
if usesResult(c): return true
proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
var typ = inferred.typ
let res = semConstExpr(c, n)
if not sameType(res.typ, typ.base):
localError(n.info,
"cannot infer the concept parameter '%s', due to a type mismatch. " &
"attempt to equate '%s' and '%s'.",
[inferred.renderTree, $res.typ, $typ.base])
typ.n = res
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# these must be last statements in a block:
const
@@ -1605,10 +1617,21 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = n.sons[i].typ
return
else:
n.sons[i] = semExpr(c, n.sons[i])
if c.inTypeClass > 0 and n[i].typ != nil:
case n[i].typ.kind
var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr
if c.inTypeClass > 0 and expr.typ != nil:
case expr.typ.kind
of tyBool:
if expr.kind == nkInfix and
expr[0].kind == nkSym and
expr[0].sym.name.s == "==":
if expr[1].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[1], expr[2])
continue
elif expr[2].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[2], expr[1])
continue
let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "type class predicate failed")
@@ -1632,8 +1655,12 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: discard
if result.len == 1 and result.sons[0].kind != nkDefer:
if result.len == 1 and
c.inTypeClass == 0 and # concept bodies should be preserved as a stmt list
result.sons[0].kind != nkDefer:
result = result.sons[0]
when defined(nimfix):
if result.kind == nkCommentStmt and not result.comment.isNil and
not (result.comment[0] == '#' and result.comment[1] == '#'):

View File

@@ -135,7 +135,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr})
let n = if n[0].kind == nkBracket: n[0] else: n
checkMinSonsLen(n, 1)
var base = semTypeNode(c, n.lastSon, nil)
var base = semTypeNode(c, n.lastSon, nil).skipTypes({tyTypeDesc})
result = newOrPrevType(kind, prev, c)
var isNilable = false
# check every except the last is an object:
@@ -155,7 +155,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
proc semVarType(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 1:
result = newOrPrevType(tyVar, prev, c)
var base = semTypeNode(c, n.sons[0], nil)
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
if base.kind == tyVar:
localError(n.info, errVarVarTypeNotAllowed)
base = base.sons[0]
@@ -185,16 +185,21 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
for i in 0..1:
rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
localError(n.info, errPureTypeMismatch)
elif not rangeT[0].isOrdinalType:
localError(n.info, errOrdinalTypeExpected)
elif enumHasHoles(rangeT[0]):
localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
let hasUnknownTypes = c.inGenericContext > 0 and
rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
if not hasUnknownTypes:
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
localError(n.info, errPureTypeMismatch)
elif not rangeT[0].isOrdinalType:
localError(n.info, errOrdinalTypeExpected)
elif enumHasHoles(rangeT[0]):
localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
for i in 0..1:
if hasGenericArguments(range[i]):
result.n.addSon makeStaticExpr(c, range[i])
result.flags.incl tfUnresolved
else:
result.n.addSon semConstExpr(c, range[i])
@@ -227,7 +232,8 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c)
proc semArrayIndex(c: PContext, n: PNode): PType =
if isRange(n): result = semRangeAux(c, n, nil)
if isRange(n):
result = semRangeAux(c, n, nil)
else:
let e = semExprWithType(c, n, {efDetermineType})
if e.typ.kind == tyFromExpr:
@@ -765,6 +771,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
let owner = if typeClass.sym != nil: typeClass.sym
else: getCurrOwner(c)
var s = newSym(skType, finalTypId, owner, info)
if sfExplain in owner.flags: s.flags.incl sfExplain
if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass)
typeClass.flags.incl tfImplicitTypeParam
@@ -843,9 +850,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
for i in 0 .. paramType.sonsLen - 2:
if paramType.sons[i].kind == tyStatic:
var x = copyNode(ast.emptyNode)
x.typ = paramType.sons[i]
result.rawAddSon makeTypeFromExpr(c, x) # aka 'tyUnknown'
var staticCopy = paramType.sons[i].exactReplica
staticCopy.flags.incl tfInferrableStatic
result.rawAddSon staticCopy
else:
result.rawAddSon newTypeS(tyAnything, c)
@@ -883,12 +890,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
for i in 1 .. <paramType.len:
let lifted = liftingWalk(paramType.sons[i])
if lifted != nil: paramType.sons[i] = lifted
when false:
if paramType.base.lastSon.kind == tyUserTypeClass:
let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true)
result = liftingWalk(expanded, true)
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true))
of tyGenericParam:
@@ -1223,6 +1231,13 @@ proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
result.sym = prev.sym
assignType(prev, result)
proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
if n.kind == nkType:
result = symFromType(n.typ, n.info)
else:
localError(n.info, errTypeExpected)
result = errorSym(c, n)
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
when defined(nimsuggest):
@@ -1237,6 +1252,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
fixupTypeOf(c, prev, typExpr)
result = typExpr.typ
if result.kind == tyTypeDesc: result.flags.incl tfExplicit
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else:
@@ -1312,7 +1328,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semTypeNode(c, whenResult, prev)
of nkBracketExpr:
checkMinSonsLen(n, 2)
var s = semTypeIdent(c, n.sons[0])
var head = n.sons[0]
var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
else: symFromExpectedTypeNode(c, semExpr(c, head))
case s.magic
of mArray: result = semArray(c, n, prev)
of mOpenArray: result = semContainer(c, n, tyOpenArray, "openarray", prev)
@@ -1344,6 +1362,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: result = semGeneric(c, n, s, prev)
of nkDotExpr:
let typeExpr = semExpr(c, n)
if typeExpr.typ.kind == tyFromExpr:
return typeExpr.typ
if typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected)
result = errorType(c)
@@ -1403,7 +1423,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy:
result = newOrPrevType(tyStatic, prev, c)
var base = semTypeNode(c, n.sons[0], nil)
var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
result.rawAddSon(base)
result.flags.incl tfHasStatic
of nkIteratorTy:

View File

@@ -122,6 +122,7 @@ proc isTypeParam(n: PNode): bool =
proc hasGenericArguments*(n: PNode): bool =
if n.kind == nkSym:
return n.sym.kind == skGenericParam or
tfInferrableStatic in n.sym.typ.flags or
(n.sym.kind == skType and
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
else:
@@ -144,7 +145,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
if isTypeParam(n[i]): needsFixing = true
if needsFixing:
n.sons[0] = newSymNode(n.sons[0].sym.owner)
return cl.c.semOverloadedCall(cl.c, n, n, {skProc})
return cl.c.semOverloadedCall(cl.c, n, n, {skProc}, {})
for i in 0 .. <n.safeLen:
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
@@ -403,6 +404,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
case t.kind
of tyGenericInvocation:
result = handleGenericInvocation(cl, t)
if result.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst
of tyGenericBody:
localError(cl.info, errCannotInstantiateX, typeToString(t))
@@ -444,10 +447,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
elif t.sons[0].kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyUserTypeClass:
of tyUserTypeClass, tyStatic:
result = t
of tyGenericInst:
of tyGenericInst, tyUserTypeClassInst:
bailout()
result = instCopyType(cl, t)
idTablePut(cl.localCache, t, result)
@@ -501,8 +504,9 @@ proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo;
result.owner = owner
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
owner: PSym): PNode =
owner: PSym, allowMetaTypes = false): PNode =
var cl = initTypeVars(p, pt, n.info, owner)
cl.allowMetaTypes = allowMetaTypes
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()

View File

@@ -22,7 +22,13 @@ type
TCandidateState* = enum
csEmpty, csMatch, csNoMatch
CandidateErrors* = seq[(PSym,int)]
CandidateError* = object
sym*: PSym
unmatchedVarParam*: int
diagnostics*: seq[string]
CandidateErrors* = seq[CandidateError]
TCandidate* = object
c*: PContext
exactMatches*: int # also misused to prefer iters over procs
@@ -49,11 +55,20 @@ type
# a distrinct type
typedescMatched*: bool
isNoCall*: bool # misused for generic type instantiations C[T]
inferredTypes: seq[PType] # inferred types during the current signature
# matching. they will be reset if the matching
# is not successful. may replace the bindings
# table in the future.
diagnostics*: seq[string] # when this is not nil, the matching process
# will collect extra diagnostics that will be
# displayed to the user.
# triggered when overload resolution fails
# or when the explain pragma is used. may be
# triggered with an idetools command in the
# future.
mutabilityProblem*: uint8 # tyVar mismatch
inheritancePenalty: int # to prefer closest father object type
errors*: CandidateErrors # additional clarifications to be displayed to the
# user if overload resolution fails
TTypeRelation* = enum # order is important!
isNone, isConvertible,
isIntConv,
@@ -101,7 +116,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
idTablePut(c.bindings, key, val.skipIntLit)
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
binding: PNode, calleeScope = -1) =
binding: PNode, calleeScope = -1, diagnostics = false) =
initCandidateAux(ctx, c, callee.typ)
c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1:
@@ -116,9 +131,9 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1
else:
c.calleeScope = calleeScope
c.diagnostics = if diagnostics: @[] else: nil
c.magic = c.calleeSym.magic
initIdTable(c.bindings)
c.errors = nil
if binding != nil and callee.kind in routineKinds:
var typeParams = callee.ast[genericParamsPos]
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
@@ -572,19 +587,22 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
result = isNone
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): TTypeRelation =
var body = ff.skipTypes({tyUserTypeClassInst})
ff, a: PType): PType =
var
typeClass = ff.skipTypes({tyUserTypeClassInst})
body = typeClass.n[3]
if c.inTypeClass > 4:
localError(body.n[3].info, $body.n[3] & " too nested for type matching")
return isNone
localError(body.info, $body & " too nested for type matching")
return nil
openScope(c)
inc c.inTypeClass
defer:
dec c.inTypeClass
closeScope(c)
var typeParams: seq[(PSym, PType)]
if ff.kind == tyUserTypeClassInst:
for i in 1 .. <(ff.len - 1):
var
@@ -593,44 +611,104 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
param: PSym
template paramSym(kind): untyped =
newSym(kind, typeParamName, body.sym, body.sym.info)
newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
case typ.kind
of tyStatic:
param = paramSym skConst
param.typ = typ.base
param.ast = typ.n
of tyUnknown:
param = paramSym skVar
param.typ = typ
else:
param = paramSym skType
param.typ = makeTypeDesc(c, typ)
block addTypeParam:
for prev in typeParams:
if prev[1].id == typ.id:
param = paramSym prev[0].kind
param.typ = prev[0].typ
break addTypeParam
case typ.kind
of tyStatic:
param = paramSym skConst
param.typ = typ.exactReplica
if typ.n == nil:
param.typ.flags.incl tfInferrableStatic
else:
param.ast = typ.n
of tyUnknown:
param = paramSym skVar
param.typ = typ.exactReplica
else:
param = paramSym skType
param.typ = if typ.isMetaType:
c.newTypeWithSons(tyInferred, @[typ])
else:
makeTypeDesc(c, typ)
typeParams.safeAdd((param, typ))
addDecl(c, param)
#echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
for param in body.n[0]:
for param in typeClass.n[0]:
var
dummyName: PNode
dummyType: PType
if param.kind == nkVarTy:
let modifier = case param.kind
of nkVarTy: tyVar
of nkRefTy: tyRef
of nkPtrTy: tyPtr
of nkStaticTy: tyStatic
of nkTypeOfExpr: tyTypeDesc
else: tyNone
if modifier != tyNone:
dummyName = param[0]
dummyType = if a.kind != tyVar: makeVarType(c, a) else: a
dummyType = c.makeTypeWithModifier(modifier, a)
if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skVar, dummyName.ident, body.sym, body.sym.info)
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, typeClass.sym, typeClass.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
#echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind
var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
if checkedBody == nil: return isNone
return isGeneric
var
oldWriteHook: type(writelnHook)
diagnostics: seq[string]
errorPrefix: string
flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or
sfExplain in typeClass.sym.flags
if collectDiagnostics:
oldWriteHook = writelnHook
# XXX: we can't write to m.diagnostics directly, because
# Nim doesn't support capturing var params in closures
diagnostics = @[]
flags = {efExplain}
writelnHook = proc (s: string) =
if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg
var checkedBody = c.semTryExpr(c, body.copyTree, flags)
if collectDiagnostics:
writelnHook = oldWriteHook
for msg in diagnostics: m.diagnostics.safeAdd msg
if checkedBody == nil: return nil
# The inferrable type params have been identified during the semTryExpr above.
# We need to put them in the current sigmatch's binding table in order for them
# to be resolvable while matching the rest of the parameters
for p in typeParams:
put(m, p[1], p[0].typ)
if ff.kind == tyUserTypeClassInst:
result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
else:
result = copyType(ff, ff.owner, true)
result.n = checkedBody
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
if rules.kind == nkWith:
@@ -649,16 +727,125 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType =
else:
result = t
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
allowUnresolved = false): PNode =
# Consider this example:
# type Value[N: static[int]] = object
# proc foo[N](a: Value[N], r: range[0..(N-1)])
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after
# N is bound to a concrete value during the matching of the first param.
# This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil)
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
allowMetaTypes = allowUnresolved)
result = c.c.semExpr(c.c, instantiated)
proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
# This is a simple integer arithimetic equation solver,
# capable of deriving the value of a static parameter in
# expressions such as (N + 5) / 2 = rhs
#
# Preconditions:
#
# * The input of this proc must be semantized
# - all templates should be expanded
# - aby constant folding possible should already be performed
#
# * There must be exactly one unresolved static parameter
#
# Result:
#
# The proc will return the inferred static type with the `n` field
# populated with the inferred value.
#
# `nil` will be returned if the inference was not possible
#
if lhs.kind in nkCallKinds and lhs[0].kind == nkSym:
case lhs[0].sym.magic
of mUnaryLt:
return inferStaticParam(lhs[1], rhs + 1)
of mAddI, mAddU, mInc, mSucc:
if lhs[1].kind == nkIntLit:
return inferStaticParam(lhs[2], rhs - lhs[1].intVal)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs - lhs[2].intVal)
of mDec, mSubI, mSubU, mPred:
if lhs[1].kind == nkIntLit:
return inferStaticParam(lhs[2], lhs[1].intVal - rhs)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs + lhs[2].intVal)
of mMulI, mMulU:
if lhs[1].kind == nkIntLit:
if rhs mod lhs[1].intVal == 0:
return inferStaticParam(lhs[2], rhs div lhs[1].intVal)
elif lhs[2].kind == nkIntLit:
if rhs mod lhs[2].intVal == 0:
return inferStaticParam(lhs[1], rhs div lhs[2].intVal)
of mDivI, mDivU:
if lhs[1].kind == nkIntLit:
if lhs[1].intVal mod rhs == 0:
return inferStaticParam(lhs[2], lhs[1].intVal div rhs)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], lhs[2].intVal * rhs)
of mShlI:
if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shr lhs[2].intVal)
of mShrI:
if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shl lhs[2].intVal)
of mUnaryMinusI:
return inferStaticParam(lhs[1], -rhs)
of mUnaryPlusI, mToInt, mToBiggestInt:
return inferStaticParam(lhs[1], rhs)
else: discard
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
lhs.typ.n = newIntNode(nkIntLit, rhs)
return lhs.typ
return nil
proc failureToInferStaticParam(n: PNode) =
let staticParam = n.findUnresolvedStatic
let name = if staticParam != nil: staticParam.sym.name.s
else: "unknown"
localError(n.info, errCannotInferStaticParam, name)
proc inferStaticsInRange(c: var TCandidate,
inferred, concrete: PType): TTypeRelation =
let lowerBound = tryResolvingStaticExpr(c, inferred.n[0],
allowUnresolved = true)
let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
allowUnresolved = true)
template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) =
var exp = e
var rhs = r
var inferred = inferStaticParam(exp, rhs)
if inferred != nil:
put(c, inferred, inferred)
return isGeneric
else:
failureToInferStaticParam exp
if lowerBound.kind == nkIntLit:
if upperBound.kind == nkIntLit:
if lengthOrd(concrete) == upperBound.intVal - lowerBound.intVal + 1:
return isGeneric
else:
return isNone
doInferStatic(c, upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
elif upperBound.kind == nkIntLit:
doInferStatic(c, lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
template subtypeCheck() =
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}:
result = isNone
@@ -689,8 +876,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
assert(aOrig != nil)
var
useTypeLoweringRuleInTypeClass = c.c.inTypeClass > 0 and
not c.isNoCall and
f.kind != tyTypeDesc and
tfExplicit notin aOrig.flags
aOrig = if useTypeLoweringRuleInTypeClass:
aOrig.skipTypes({tyTypeDesc, tyFieldAccessor})
else:
aOrig
if aOrig.kind == tyInferred:
let prev = aOrig.previouslyInferred
if prev != nil:
return typeRel(c, f, prev)
else:
var candidate = f
case f.kind
of tyGenericParam:
var prev = PType(idTableGet(c.bindings, f))
if prev != nil: candidate = prev
of tyFromExpr:
let computedType = tryResolvingStaticExpr(c, f.n).typ
case computedType.kind
of tyTypeDesc:
candidate = computedType.base
of tyStatic:
candidate = computedType
else:
localError(f.n.info, errTypeExpected)
else:
discard
result = typeRel(c, aOrig.base, candidate)
if result != isNone:
c.inferredTypes.safeAdd aOrig
aOrig.sons.add candidate
result = isEqual
return
# var and static arguments match regular modifier-free types
let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
# XXX: Theoretically, maybeSkipDistinct could be called before we even
# start the param matching process. This could be done in `prepareOperand`
# for example, but unfortunately `prepareOperand` is not called in certain
@@ -702,6 +930,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
tyGenericInst, tyGenericParam} + tyTypeClasses:
return typeRel(c, f, lastSon(a))
if a.isResolvedUserTypeClass:
return typeRel(c, f, a.lastSon)
template bindingRet(res) =
if doBind:
let bound = aOrig.skipTypes({tyRange}).skipIntLit
@@ -807,6 +1038,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
case a.kind
of tyArray:
var fRange = f.sons[0]
var aRange = a.sons[0]
if fRange.kind == tyGenericParam:
var prev = PType(idTableGet(c.bindings, fRange))
if prev == nil:
@@ -814,28 +1046,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
fRange = a
else:
fRange = prev
result = typeRel(c, f.sons[1], a.sons[1])
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
a.sons[1].skipTypes({tyTypeDesc}))
if result < isGeneric: return isNone
if rangeHasStaticIf(fRange):
if tfUnresolved in fRange.flags:
# This is a range from an array instantiated with a generic
# static param. We must extract the static param here and bind
# it to the size of the currently supplied array.
var
rangeStaticT = fRange.getStaticTypeFromRange
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
inputUpperBound = a.sons[0].n[1].intVal
# we must correct for the off-by-one discrepancy between
# ranges and static params:
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
put(c, rangeStaticT, replacementT)
return isGeneric
let len = tryResolvingStaticExpr(c, fRange.n[1])
if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
return # if we get this far, the result is already good
else:
return isNone
if fRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, fRange, a)
elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, aRange, f)
elif lengthOrd(fRange) != lengthOrd(a):
result = isNone
else: discard
@@ -1111,11 +1329,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else:
return isNone
of tyUserTypeClass, tyUserTypeClassInst:
considerPreviousT:
result = matchUserTypeClass(c.c, c, f, aOrig)
if result == isGeneric:
put(c, f, a)
of tyUserTypeClassInst, tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = typeRel(c, f.lastSon, a)
else:
var matched = matchUserTypeClass(c.c, c, f, aOrig)
if matched != nil:
bindConcreteTypeToUserTypeClass(matched, a)
put(c, f, matched)
result = isGeneric
else:
result = isNone
of tyCompositeTypeClass:
considerPreviousT:
@@ -1212,6 +1436,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# XXX endless recursion?
#result = typeRel(c, prev, aOrig)
result = isNone
of tyInferred:
let prev = f.previouslyInferred
if prev != nil:
result = typeRel(c, prev, a)
else:
result = typeRel(c, f.base, a)
if result != isNone:
c.inferredTypes.safeAdd f
f.sons.add a
of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f))
if prev == nil:
@@ -1355,12 +1590,12 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
of isEqual: inc(m.exactMatches)
of isNone: discard
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
argSemantized, argOrig: PNode): PNode =
var
fMaybeStatic = f.skipTypes({tyDistinct})
arg = argSemantized
argType = argType
a = a
c = m.c
if tfHasStatic in fMaybeStatic.flags:
@@ -1370,15 +1605,15 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder
# and finally start using tyTypedesc for generic types properly.
if argType.kind == tyGenericParam and tfWildcard in argType.flags:
argType.assignType(f)
# put(m.bindings, f, argType)
if a.kind == tyGenericParam and tfWildcard in a.flags:
a.assignType(f)
# put(m.bindings, f, a)
return argSemantized
if argType.kind == tyStatic:
if a.kind == tyStatic:
if m.callee.kind == tyGenericBody and
argType.n == nil and
tfGenericTypeParam notin argType.flags:
a.n == nil and
tfGenericTypeParam notin a.flags:
return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
else:
var evaluated = c.semTryConstExpr(c, arg)
@@ -1386,9 +1621,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
arg.typ = newTypeS(tyStatic, c)
arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated
argType = arg.typ
a = arg.typ
var a = argType
var r = typeRel(m, f, a)
if r != isNone and m.calleeSym != nil and
@@ -1830,6 +2064,10 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
setSon(m.call, formal.position + 1, def)
inc(f)
# forget all inferred types if the overload matching failed
if m.state == csNoMatch:
for t in m.inferredTypes:
if t.sonsLen > 1: t.sons.setLen 1
proc argtypeMatches*(c: PContext, f, a: PType): bool =
var m: TCandidate

View File

@@ -55,16 +55,17 @@ const
# TODO: Remove tyTypeDesc from each abstractX and (where necessary)
# replace with typedescX
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias}
tyTypeDesc, tyAlias, tyInferred}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc,
tyAlias}
tyAlias, tyInferred}
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc,
tyAlias}
tyAlias, tyInferred}
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias}
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias}
tyTypeDesc, tyAlias, tyInferred}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
tyInferred}
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias,
tyInferred}
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {tyTypeDesc}
@@ -182,7 +183,7 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
if result: return
if not containsOrIncl(marker, t.id):
case t.kind
of tyGenericInst, tyGenericBody, tyAlias:
of tyGenericInst, tyGenericBody, tyAlias, tyInferred:
result = iterOverTypeAux(marker, lastSon(t), iter, closure)
else:
for i in countup(0, sonsLen(t) - 1):
@@ -410,12 +411,19 @@ const
"unused0", "unused1",
"unused2", "varargs[$1]", "unused", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
"void"]
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
tc.sons.safeAdd concrete
tc.flags.incl tfResolved
template isResolvedUserTypeClass*(t: PType): bool =
tfResolved in t.flags
proc addTypeFlags(name: var string, typ: PType) {.inline.} =
if tfNotNil in typ.flags: name.add(" not nil")
@@ -460,6 +468,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil
if t.isResolvedUserTypeClass: return typeToString(t.lastSon)
return t.sym.owner.name.s
of tyBuiltInTypeClass:
result = case t.base.kind:
@@ -476,6 +485,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyTuple: "tuple"
of tyOpenArray: "openarray"
else: typeToStr[t.base.kind]
of tyInferred:
let concrete = t.previouslyInferred
if concrete != nil: result = typeToString(concrete)
else: result = "inferred[" & typeToString(t.base) & "]"
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
@@ -971,7 +984,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
sameValue(a.n.sons[0], b.n.sons[0]) and
sameValue(a.n.sons[1], b.n.sons[1])
of tyGenericInst, tyAlias: discard
of tyGenericInst, tyAlias, tyInferred:
cycleCheck()
result = sameTypeAux(a.lastSon, b.lastSon, c)
of tyNone: result = false
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("sameFlags")
@@ -1118,7 +1133,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = nil
of tyOrdinal:
if kind != skParam: result = t
of tyGenericInst, tyDistinct, tyAlias:
of tyGenericInst, tyDistinct, tyAlias, tyInferred:
result = typeAllowedAux(marker, lastSon(t), kind, flags)
of tyRange:
if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
@@ -1305,14 +1320,20 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
if result < 0: return
if a < maxAlign: a = maxAlign
result = align(result, a)
of tyInferred:
if typ.len > 1:
result = computeSizeAux(typ.lastSon, a)
of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
result = computeSizeAux(lastSon(typ), a)
of tyTypeClasses:
result = if typ.isResolvedUserTypeClass: computeSizeAux(typ.lastSon, a)
else: szUnknownSize
of tyTypeDesc:
result = computeSizeAux(typ.base, a)
of tyForward: return szIllegalRecursion
of tyStatic:
if typ.n != nil: result = computeSizeAux(lastSon(typ), a)
else: result = szUnknownSize
result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
else: szUnknownSize
else:
#internalError("computeSizeAux()")
result = szUnknownSize
@@ -1333,18 +1354,17 @@ proc getSize(typ: PType): BiggestInt =
if result < 0: internalError("getSize: " & $typ.kind)
proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
if t.kind == tyStatic:
case t.kind
of tyStatic:
return t.n == nil
if t.kind == tyTypeDesc:
of tyTypeDesc:
if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true
return false
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
of GenericTypes + tyTypeClasses + {tyFromExpr}:
return true
return false
else:
return false
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)

View File

@@ -175,7 +175,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[i], info)
else:
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
of tyGenericBody, tyOrdinal, tyUserTypeClassInst:
of tyGenericBody, tyOrdinal:
result = mapTypeToAst(t.lastSon, info)
of tyDistinct:
if inst:
@@ -285,15 +285,19 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
of tyProxy: result = atomicType("error", mNone)
of tyBuiltInTypeClass:
result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
of tyUserTypeClass:
result = mapTypeToBracket("concept", mNone, t, info)
result.add t.n.copyTree
of tyUserTypeClass, tyUserTypeClassInst:
if t.isResolvedUserTypeClass:
result = mapTypeToAst(t.lastSon, info)
else:
result = mapTypeToBracket("concept", mNone, t, info)
result.add t.n.copyTree
of tyCompositeTypeClass:
result = mapTypeToBracket("compositeTypeClass", mNone, t, info)
of tyAnd: result = mapTypeToBracket("and", mAnd, t, info)
of tyOr: result = mapTypeToBracket("or", mOr, t, info)
of tyNot: result = mapTypeToBracket("not", mNot, t, info)
of tyAnything: result = atomicType("anything", mNone)
of tyInferred: internalAssert false
of tyStatic, tyFromExpr, tyFieldAccessor:
if inst:
if t.n != nil: result = t.n.copyTree

View File

@@ -1678,7 +1678,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
elif sfImportc in s.flags: c.importcSym(n.info, s)
genLit(c, n, dest)
of skConst:
gen(c, s.ast, dest)
let constVal = if s.ast != nil: s.ast else: s.typ.n
gen(c, constVal, dest)
of skEnumField:
if dest < 0: dest = c.getTemp(n.typ)
if s.position >= low(int16) and s.position <= high(int16):

View File

@@ -66,7 +66,7 @@ type
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
wPartial,
wPartial, wExplain,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -152,7 +152,7 @@ const
"computedgoto", "injectstmt", "experimental",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"guard", "locks", "partial",
"guard", "locks", "partial", "explain",
"auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double",

View File

@@ -54,9 +54,6 @@ Advanced options:
--embedsrc embeds the original source code as comments
in the generated output
--threadanalysis:on|off turn thread analysis on|off
--reportConceptFailures:on|off
show errors for 'system.compiles' and concept
evaluation
--tlsEmulation:on|off turn thread local storage emulation on|off
--taintMode:on|off turn taint mode on|off
--implicitStatic:on|off turn implicit compile time evaluation on|off

View File

@@ -116,7 +116,8 @@ type class matches
``array`` any array type
``set`` any set type
``seq`` any seq type
``any`` any type
``auto`` any type
``any`` distinct auto (see below)
================== ===================================================
Furthermore, every generic type automatically creates a type class of the same
@@ -148,8 +149,8 @@ as `type constraints`:idx: of the generic type parameter:
onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
By default, during overload resolution each named type class will bind to
exactly one concrete type. Here is an example taken directly from the system
module to illustrate this:
exactly one concrete type. We call such type classes `bind once`:idx: types.
Here is an example taken directly from the system module to illustrate this:
.. code-block:: nim
proc `==`*(x, y: tuple): bool =
@@ -161,7 +162,8 @@ module to illustrate this:
if a != b: result = false
Alternatively, the ``distinct`` type modifier can be applied to the type class
to allow each param matching the type class to bind to a different type.
to allow each param matching the type class to bind to a different type. Such
type classes are called `bind many`:idx: types.
Procs written with the implicitly generic style will often need to refer to the
type parameters of the matched generic type. They can be easily accessed using
@@ -211,34 +213,421 @@ Concepts are written in the following form:
Comparable = concept x, y
(x < y) is bool
Container[T] = concept c
c.len is Ordinal
items(c) is T
for value in c:
type(value) is T
Stack[T] = concept s, var v
s.pop() is T
v.push(T)
s.len is Ordinal
for value in s:
value is T
The concept is a match if:
a) all of the expressions within the body can be compiled for the tested type
b) all statically evaluatable boolean expressions in the body must be true
b) all statically evaluable boolean expressions in the body must be true
The identifiers following the ``concept`` keyword represent instances of the
currently matched type. These instances can act both as variables of the type,
when used in contexts where a value is expected, and as the type itself when
used in contexts where a type is expected.
currently matched type. You can apply any of the standard type modifiers such
as ``var``, ``ref``, ``ptr`` and ``static`` to denote a more specific type of
instance. You can also apply the `type` modifier to create a named instance of
the type itself:
.. code-block:: nim
type
MyConcept = concept x, var v, ref r, ptr p, static s, type T
...
Within the concept body, types can appear in positions where ordinary values
and parameters are expected. This provides a more convenient way to check for
the presence of callable symbols with specific signatures:
.. code-block:: nim
type
OutputStream = concept var s
s.write(string)
In order to check for symbols accepting ``typedesc`` params, you must prefix
the type with an explicit ``type`` modifier. The named instance of the type,
following the ``concept`` keyword is also considered an explicit ``typedesc``
value that will be matched only as a type.
.. code-block:: nim
type
# Let's imagine a user-defined casting framework with operators
# such as `val.to(string)` and `val.to(JSonValue)`. We can test
# for these with the following concept:
MyCastables = concept x
x.to(type string)
x.to(type JSonValue)
# Let's define a couple of concepts, known from Algebra:
AdditiveMonoid* = concept x, y, type T
x + y is T
T.zero is T # require a proc such as `int.zero` or 'Position.zero'
AdditiveGroup* = concept x, y, type T
x is AdditiveMonoid
-x is T
x - y is T
Please note that the ``is`` operator allows one to easily verify the precise
type signatures of the required operations, but since type inference and
default parameters are still applied in the provided block, it's also possible
to encode usage protocols that do not reveal implementation details.
default parameters are still applied in the concept body, it's also possible
to describe usage protocols that do not reveal implementation details.
Much like generics, concepts are instantiated exactly
once for each tested type and any static code included within them is also
executed once.
Much like generics, concepts are instantiated exactly once for each tested type
and any static code included within the body is executed only once.
**Hint**: Since concepts are still very rough at the edges there is a
command line switch ``--reportConceptFailures:on`` to make debugging
concept related type failures more easy.
Concept diagnostics
-------------------
By default, the compiler will report the matching errors in concepts only when
no other overload can be selected and a normal compilation error is produced.
When you need to understand why the compiler is not matching a particular
concept and, as a result, a wrong overload is selected, you can apply the
``explain`` pragma to either the concept body or a particular call-site.
.. code-block:: nim
type
MyConcept {.explain.} = concept ...
overloadedProc(x, y, z) {.explain.}
This will provide Hints in the compiler output either every time the concept is
not matched or only on the particular call-site.
Generic concepts and type binding rules
---------------------------------------
The concept types can be parametric just like the regular generic types:
.. code-block:: nim
### matrixalgo.nim
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
M.ValueType is T
M.Rows == R
M.Cols == C
m[int, int] is T
mvar[int, int] = T
type TransposedType = stripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
AnyTransform3D* = AnyMatrix[4, 4, float]
proc transposed*(m: AnyMatrix): m.TransposedType =
for r in 0 .. <m.R:
for c in 0 .. <m.C:
result[r, c] = m[c, r]
proc determinant*(m: AnySquareMatrix): int =
...
proc setPerspectiveProjection*(m: AnyTransform3D) =
...
--------------
### matrix.nim
type
Matrix*[M, N: static[int]; T] = object
data: array[M*N, T]
proc `[]`*(M: Matrix; m, n: int): M.T =
M.data[m * M.N + n]
proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
M.data[m * M.N + n] = v
# Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M
template Cols*(M: type Matrix): expr = M.N
template ValueType*(M: type Matrix): typedesc = M.T
-------------
### usage.nim
import matrix, matrixalgo
var
m: Matrix[3, 3, int]
projectionMatrix: Matrix[4, 4, float]
echo m.transposed.determinant
setPerspectiveProjection projectionMatrix
When the concept type is matched against a concrete type, the unbound type
parameters are inferred from the body of the concept in a way that closely
resembles the way generic parameters of callable symbols are inferred on
call sites.
Unbound types can appear both as params to calls such as `s.push(T)` and
on the right-hand side of the ``is`` operator in cases such as `x.pop is T`
and `x.data is seq[T]`.
Unbound static params will be inferred from expressions involving the `==`
operator and also when types dependent on them are being matched:
.. code-block:: nim
type
MatrixReducer[M, N: static[int]; T] = concept x
x.reduce(SquareMatrix[N, T]) is array[M, int]
The Nim compiler includes a simple linear equation solver, allowing it to
infer static params in some situations where integer arithmetic is involved.
Just like in regular type classes, Nim discriminates between ``bind once``
and ``bind many`` types when matching the concept. You can add the ``distinct``
modifier to any of the otherwise inferable types to get a type that will be
matched without permanently inferring it. This may be useful when you need
to match several procs accepting the same wide class of types:
.. code-block:: nim
type
Enumerable[T] = concept e
for v in e:
v is T
type
MyConcept = concept o
# this could be inferred to a type such as Enumerable[int]
o.foo is distinct Enumerable
# this could be inferred to a different type such as Enumerable[float]
o.bar is distinct Enumerable
# it's also possible to give an alias name to a `bind many` type class
type Enum = distinct Enumerable
o.baz is Enum
On the other hand, using ``bind once`` types allows you to test for equivalent
types used in multiple signatures, without actually requiring any concrete
types, thus allowing you to encode implementation-defined types:
.. code-block:: nim
type
MyConcept = concept x
type T1 = auto
x.foo(T1)
x.bar(T1) # both procs must accept the same type
type T2 = seq[SomeNumber]
x.alpha(T2)
x.omega(T2) # both procs must accept the same type
# and it must be a numeric sequence
As seen in the previous examples, you can refer to generic concepts such as
`Enumerable[T]` just by their short name. Much like the regular generic types,
the concept will be automatically instantiated with the bind once auto type
in the place of each missing generic param.
Please note that generic concepts such as `Enumerable[T]` can be matched
against concrete types such as `string`. Nim doesn't require the concept
type to have the same number of parameters as the type being matched.
If you wish to express a requirement towards the generic parameters of
the matched type, you can use a type mapping operator such as `genericHead`
or `stripGenericParams` within the body of the concept to obtain the
uninstantiated version of the type, which you can then try to instantiate
in any required way. For example, here is how one might define the classic
`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
type is an instance of it:
.. code-block:: nim
import future, typetraits
type
Functor[A] = concept f
type MatchedGenericType = genericHead(f.type)
# `f` will be a value of a type such as `Option[T]`
# `MatchedGenericType` will become the `Option` type
f.val is A
# The Functor should provide a way to obtain
# a value stored inside it
type T = auto
map(f, A -> T) is MatchedGenericType[T]
# And it should provide a way to map one instance of
# the Functor to a instance of a different type, given
# a suitable `map` operation for the enclosed values
import options
echo Option[int] is Functor # prints true
Concept derived values
----------------------
All top level constants or types appearing within the concept body are
accessible through the dot operator in procs where the concept was successfully
matched to a concrete type:
.. code-block:: nim
type
DateTime = concept t1, t2, type T
const Min = T.MinDate
T.Now is T
t1 < t2 is bool
type TimeSpan = type(t1 - t2)
TimeSpan * int is TimeSpan
TimeSpan + TimeSpan is TimeSpan
t1 + TimeSpan is T
proc eventsJitter(events: Enumerable[DateTime]): float =
var
# this variable will have the inferred TimeSpan type for
# the concrete Date-like value the proc was called with:
averageInterval: DateTime.TimeSpan
deviation: float
...
Concept refinement
------------------
When the matched type within a concept is directly tested against a different
concept, we say that the outer concept is a refinement of the inner concept and
thus it is more-specific. When both concepts are matched in a call during
overload resolution, Nim will assign a higher precedence to the most specific
one. As an alternative way of defining concept refinements, you can use the
object inheritance syntax involving the ``of`` keyword:
.. code-block:: nim
type
Graph = concept g, type G of EqualyComparable, Copyable
type
VertexType = G.VertexType
EdgeType = G.EdgeType
VertexType is Copyable
EdgeType is Copyable
var
v: VertexType
e: EdgeType
IncidendeGraph = concept of Graph
# symbols such as variables and types from the refined
# concept are automatically in scope:
g.source(e) is VertexType
g.target(e) is VertexType
g.outgoingEdges(v) is Enumerable[EdgeType]
BidirectionalGraph = concept g, type G
# The following will also turn the concept into a refinement when it
# comes to overload resolution, but it doesn't provide the convenient
# symbol inheritance
g is IncidendeGraph
g.incomingEdges(G.VertexType) is Enumerable[G.EdgeType]
proc f(g: IncidendeGraph)
proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
# matching the BidirectionalGraph concept
Converter type classes
----------------------
Concepts can also be used to convert a whole range of types to a single type or
a small set of simpler types. This is achieved with a `return` statement within
the concept body:
.. code-block:: nim
type
Stringable = concept x
$x is string
return $x
StringRefValue[CharType] = object
base: ptr CharType
len: int
StringRef = concept x
# the following would be an overloaded proc for cstring, string, seq and
# other user-defined types, returning either a StringRefValue[char] or
# StringRefValue[wchar]
return makeStringRefValue(x)
# the varargs param will here be converted to an array of StringRefValues
# the proc will have only two instantiations for the two character types
proc log(format: static[string], varargs[StringRef])
# this proc will allow char and wchar values to be mixed in
# the same call at the cost of additional instantiations
# the varargs param will be converted to a tuple
proc log(format: static[string], varargs[distinct StringRef])
VTable types
------------
Concepts allow Nim to define a great number of algorithms, using only
static polymorphism and without erasing any type information or sacrificing
any execution speed. But when polymorphic collections of objects are required,
the user must use one of the provided type erasure techniques - either common
base types or VTable types.
VTable types are represented as "fat pointers" storing a reference to an
object together with a reference to a table of procs implementing a set of
required operations (the so called vtable).
In contrast to other programming languages, the vtable in Nim is stored
externally to the object, allowing you to create multiple different vtable
views for the same object. Thus, the polymorphism in Nim is unbounded -
any type can implement an unlimited number of protocols or interfaces not
originally envisioned by the type's author.
Any concept type can be turned into a VTable type by using the ``vtref``
or the ``vtptr`` compiler magics. Under the hood, these magics generate
a converter type class, which converts the regular instances of the matching
types to the corresponding VTable type.
.. code-block:: nim
type
IntEnumerable = vtref Enumerable[int]
MyObject = object
enumerables: seq[IntEnumerable]
streams: seq[OutputStream.vtref]
proc addEnumerable(o: var MyObject, e: IntEnumerable) =
o.enumerables.add e
proc addStream(o: var MyObject, e: OutputStream.vtref) =
o.streams.add e
The procs that will be included in the vtable are derived from the concept
body and include all proc calls for which all param types were specified as
concrete types. All such calls should include exactly one param of the type
matched against the concept (not necessarily in the first position), which
will be considered the value bound to the vtable.
Overloads will be created for all captured procs, accepting the vtable type
in the position of the captured underlying object.
Under these rules, it's possible to obtain a vtable type for a concept with
unbound type parameters or one instantiated with metatypes (type classes),
but it will include a smaller number of captured procs. A completely empty
vtable will be reported as an error.
The ``vtref`` magic produces types which can be bound to ``ref`` types and
the ``vtptr`` magic produced types bound to ``ptr`` types.
Symbol lookup in generics

View File

@@ -19,7 +19,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
##
## import typetraits
##
## proc `$`*[T](some:typedesc[T]): string = name(T)
## proc `$`*(T: typedesc): string = name(T)
##
## template test(x): stmt =
## echo "type: ", type(x), ", value: ", x
@@ -31,6 +31,21 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
## test(@['A','B'])
## # --> type: seq[char], value: @[A, B]
proc arity*(t: typedesc): int {.magic: "TypeTrait".}
## Returns the arity of the given type
proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".}
## Accepts an instantiated generic type and returns its
## uninstantiated form.
##
## For example:
## seq[int].genericHead will be just seq
## seq[int].genericHead[float] will be seq[float]
##
## A compile-time error will be produced if the supplied type
## is not generic
proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
## This trait is similar to `genericHead`, but instead of producing
## error for non-generic types, it will just return them unmodified

15
tests/concepts/matrix.nim Normal file
View File

@@ -0,0 +1,15 @@
type
Matrix*[M, N: static[int]; T] = object
data: array[M*N, T]
proc `[]`*(M: Matrix; m, n: int): M.T =
M.data[m * M.N + n]
proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
M.data[m * M.N + n] = v
# Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M
template Cols*(M: type Matrix): expr = M.N
template ValueType*(M: type Matrix): typedesc = M.T

View File

@@ -0,0 +1,28 @@
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
M.ValueType is T
M.Rows == R
M.Cols == C
m[int, int] is T
mvar[int, int] = T
type TransposedType = stripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
AnyTransform3D* = AnyMatrix[4, 4, float]
proc transposed*(m: AnyMatrix): m.TransposedType =
for r in 0 .. <m.R:
for c in 0 .. <m.C:
result[r, c] = m[c, r]
proc determinant*(m: AnySquareMatrix): int =
return 0
proc setPerspectiveProjection*(m: AnyTransform3D) =
discard

21
tests/concepts/t1128.nim Normal file
View File

@@ -0,0 +1,21 @@
discard """
output: "true\ntrue"
"""
type
TFooContainer[T] = object
TContainer[T] = generic var c
foo(c, T)
proc foo[T](c: var TFooContainer[T], val: T) =
discard
proc bar(c: var TContainer) =
discard
var fooContainer: TFooContainer[int]
echo fooContainer is TFooContainer # true.
echo fooContainer is TFooContainer[int] # true.
fooContainer.bar()

41
tests/concepts/t3330.nim Normal file
View File

@@ -0,0 +1,41 @@
discard """
errormsg: "type mismatch: got (Bar[system.int])"
nimout: '''
t3330.nim(40, 4) Error: type mismatch: got (Bar[system.int])
but expected one of:
proc test(foo: Foo[int])
t3330.nim(25, 8) Hint: Non-matching candidates for add(k, string, T)
proc add[T](x: var seq[T]; y: T)
proc add(result: var string; x: float)
proc add(x: var string; y: string)
proc add(x: var string; y: cstring)
proc add(x: var string; y: char)
proc add(result: var string; x: int64)
proc add[T](x: var seq[T]; y: openArray[T])
t3330.nim(25, 8) template/generic instantiation from here
t3330.nim(32, 6) Foo: 'bar.value' cannot be assigned to
t3330.nim(25, 8) template/generic instantiation from here
t3330.nim(33, 6) Foo: 'bar.x' cannot be assigned to
'''
"""
type
Foo[T] = concept k
add(k, string, T)
Bar[T] = object
value: T
x: string
proc add[T](bar: Bar[T], x: string, val: T) =
bar.value = val
bar.x = x
proc test(foo: Foo[int]) =
foo.add("test", 42)
echo(foo.x)
var bar = Bar[int]()
bar.test()

32
tests/concepts/t976.nim Normal file
View File

@@ -0,0 +1,32 @@
import macros
type
int1 = distinct int
int2 = distinct int
int1g = concept x
x is int1
int2g = concept x
x is int2
proc take[T: int1g](value: int1) =
when T is int2:
static: error("killed in take(int1)")
proc take[T: int2g](vale: int2) =
when T is int1:
static: error("killed in take(int2)")
var i1: int1 = 1.int1
var i2: int2 = 2.int2
take[int1](i1)
take[int2](i2)
template reject(e) =
static: assert(not compiles(e))
reject take[string](i2)
reject take[int1](i2)

View File

@@ -0,0 +1,69 @@
discard """
output: '''x as ParameterizedType[T]
x as ParameterizedType[T]
x as ParameterizedType[T]
x as ParameterizedType
x as ParameterizedType
x as CustomTypeClass'''
"""
type ParameterizedType[T] = object
type CustomTypeClass = concept
true
# 3 competing procs
proc a[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
proc a(x: ParameterizedType) =
echo "x as ParameterizedType"
proc a(x: CustomTypeClass) =
echo "x as CustomTypeClass"
# the same procs in different order
proc b(x: ParameterizedType) =
echo "x as ParameterizedType"
proc b(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc b[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
# and yet another order
proc c(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc c(x: ParameterizedType) =
echo "x as ParameterizedType"
proc c[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
# remove the most specific one
proc d(x: ParameterizedType) =
echo "x as ParameterizedType"
proc d(x: CustomTypeClass) =
echo "x as CustomTypeClass"
# then shuffle the order again
proc e(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc e(x: ParameterizedType) =
echo "x as ParameterizedType"
# the least specific one is a match
proc f(x: CustomTypeClass) =
echo "x as CustomTypeClass"
a(ParameterizedType[int]())
b(ParameterizedType[int]())
c(ParameterizedType[int]())
d(ParameterizedType[int]())
e(ParameterizedType[int]())
f(ParameterizedType[int]())

120
tests/concepts/texplain.nim Normal file
View File

@@ -0,0 +1,120 @@
discard """
cmd: "nim c --verbosity:0 --colors:off $file"
nimout: '''
texplain.nim(99, 10) Hint: Non-matching candidates for e(y)
proc e(i: int): int
texplain.nim(102, 7) Hint: Non-matching candidates for e(10)
proc e(o: ExplainedConcept): int
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(105, 10) Hint: Non-matching candidates for e(10)
proc e(o: ExplainedConcept): int
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(109, 20) Error: type mismatch: got (NonMatchingType)
but expected one of:
proc e(o: ExplainedConcept): int
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
proc e(i: int): int
texplain.nim(110, 20) Error: type mismatch: got (NonMatchingType)
but expected one of:
proc r(o: RegularConcept): int
texplain.nim(69, 5) RegularConcept: type class predicate failed
proc r[T](a: SomeNumber; b: T; c: auto)
proc r(i: string): int
texplain.nim(111, 20) Hint: Non-matching candidates for r(y)
proc r[T](a: SomeNumber; b: T; c: auto)
proc r(i: string): int
texplain.nim(119, 2) Error: type mismatch: got (MatchingType)
but expected one of:
proc f(o: NestedConcept)
texplain.nim(69, 6) RegularConcept: undeclared field: 'foo'
texplain.nim(69, 6) RegularConcept: undeclared field: '.'
texplain.nim(69, 6) RegularConcept: expression '.' cannot be called
texplain.nim(69, 5) RegularConcept: type class predicate failed
texplain.nim(70, 6) RegularConcept: undeclared field: 'bar'
texplain.nim(70, 6) RegularConcept: undeclared field: '.'
texplain.nim(70, 6) RegularConcept: expression '.' cannot be called
texplain.nim(69, 5) RegularConcept: type class predicate failed
texplain.nim(73, 5) NestedConcept: type class predicate failed
'''
line: 119
errormsg: "type mismatch: got (MatchingType)"
"""
type
ExplainedConcept {.explain.} = concept o
o.foo is int
o.bar is string
RegularConcept = concept o
o.foo is int
o.bar is string
NestedConcept = concept o
o.foo is RegularConcept
NonMatchingType = object
foo: int
bar: int
MatchingType = object
foo: int
bar: string
proc e(o: ExplainedConcept): int = 1
proc e(i: int): int = i
proc r[T](a: SomeNumber, b: T, c: auto) = discard
proc r(o: RegularConcept): int = 1
proc r(i: string): int = 1
proc f(o: NestedConcept) = discard
var n = NonMatchingType(foo: 10, bar: 20)
var y = MatchingType(foo: 10, bar: "bar")
# no diagnostic here:
discard e(y)
# explain that e(int) doesn't match
discard e(y) {.explain.}
# explain that e(ExplainedConcept) doesn't match
echo(e(10) {.explain.}, 20)
# explain that e(ExplainedConcept) doesn't again
discard e(10)
static:
# provide diagnostics why the compile block failed
assert(compiles(e(n)) {.explain.} == false)
assert(compiles(r(n)) {.explain.} == false)
assert(compiles(r(y)) {.explain.} == true)
# these should not produce any output
assert(compiles(r(10)) == false)
assert(compiles(e(10)) == true)
# finally, provide multiple nested explanations for failed matching
# of regular concepts, even when the explain pragma is not used
f(y)

View File

@@ -0,0 +1,102 @@
discard """
output: '''10
10
nil
1'''
msg: '''
K=string V=int
K=int64 V=string
K=int V=int
'''
"""
import tables, typetraits
template ok(check) = assert check
template no(check) = assert(not check)
type
Enumerable[T] = concept e
for v in e:
v is T
Map[K, V] = concept m, var mvar
m[K] is V
mvar[K] = V
m.contains(K) is bool
m.valuesSeq is Enumerable[V]
TreeMap[K, V] = object
root: int
SparseSeq = object
data: seq[int]
JudyArray = object
data: SparseSeq
static:
ok seq[int] is Enumerable[int]
ok seq[string] is Enumerable
ok seq[int] is Enumerable[SomeNumber]
ok SparseSeq.data is Enumerable
no seq[string] is Enumerable[int]
no int is Enumerable
no int is Enumerable[int]
# Complete the map concept implementation for the Table type
proc valuesSeq[K, V](t: Table[K, V]): seq[V] =
result = @[]
for k, v in t:
result.add v
# Map concept inplementation for TreeMap
proc valuesSeq(t: TreeMap): array[1, TreeMap.V] =
var v: t.V
result = [v]
proc contains[K, V](t: TreeMap[K, V], key: K): bool = true
proc `[]=`[K, V](t: var TreeMap[K, V], key: K, val: V) = discard
proc `[]`(t: TreeMap, key: t.K): TreeMap.V = discard
# Map concept implementation for the non-generic JudyArray
proc valuesSeq(j: JudyArray): SparseSeq = j.data
proc contains(t: JudyArray, key: int): bool = true
proc `[]=`(t: var JudyArray, key, val: int) = discard
proc `[]`(t: JudyArray, key: int): int = discard
iterator items(s: SparseSeq): int =
for i in s.data: yield i
# Generic proc defined over map
proc getFirstValue[K,V](m : Map[K,V]): V =
static: echo "K=", K.name, " V=", V.name
for i in m.valuesSeq:
return i
raise newException(RangeError, "no values")
proc useConceptProcInGeneric[K, V](t: Table[K, V]): V =
return t.getFirstValue
var t = initTable[string, int]()
t["test"] = 10
echo t.getFirstValue
echo t.useConceptProcInGeneric
var tm = TreeMap[int64, string](root: 0)
echo getFirstValue(tm)
var j = JudyArray(data: SparseSeq(data: @[1, 2, 3]))
echo getFirstValue(j)
static:
ok Table[int, float] is Map
ok Table[int, string] is Map[SomeNumber, string]
no JudyArray is Map[string, int]

View File

@@ -0,0 +1,81 @@
discard """
output: "0\n0\n0"
msg: '''
R=3 C=3 TE=9 FF=14 FC=20 T=int
R=3 C=3 T=int
'''
"""
import typetraits
template ok(x) = assert x
template no(x) = assert(not x)
const C = 10
type
Matrix[Rows, Cols, TotalElements, FromFoo, FromConst: static[int]; T] = concept m, var mvar, type M
M.M == Rows
Cols == M.N
M.T is T
m[int, int] is T
mvar[int, int] = T
FromConst == C * 2
# more complicated static param inference cases
m.data is array[TotalElements, T]
m.foo(array[0..FromFoo, type m[int, 10]])
MyMatrix[M, K: static[int]; T] = object
data: array[M*K, T]
# adaptor for the concept's non-matching expectations
template N(M: type MyMatrix): expr = M.K
proc `[]`(m: MyMatrix; r, c: int): m.T =
m.data[r * m.K + c]
proc `[]=`(m: var MyMatrix; r, c: int, v: m.T) =
m.data[r * m.K + c] = v
proc foo(x: MyMatrix, arr: array[15, x.T]) = discard
proc genericMatrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T =
static:
echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
m[0, 0]
proc implicitMatrixProc(m: Matrix): m.T =
static:
echo "R=", m.Rows,
" C=", m.Cols,
# XXX: fix these
#" TE=", m.TotalElements,
#" FF=", m.FromFoo,
#" FC=", m.FromConst,
" T=", m.T.name
m[0, 0]
proc myMatrixProc(x: MyMatrix): MyMatrix.T = genericMatrixProc(x)
var x: MyMatrix[3, 3, int]
static:
# ok x is Matrix
ok x is Matrix[3, 3, 9, 14, 20, int]
no x is Matrix[3, 3, 8, 15, 20, int]
no x is Matrix[3, 3, 9, 10, 20, int]
no x is Matrix[3, 3, 9, 15, 21, int]
no x is Matrix[3, 3, 9, 15, 20, float]
no x is Matrix[4, 3, 9, 15, 20, int]
no x is Matrix[3, 4, 9, 15, 20, int]
echo x.myMatrixProc
echo x.genericMatrixProc
echo x.implicitMatrixProc

View File

@@ -0,0 +1,31 @@
discard """
output: "0"
"""
import matrix, matrixalgo
import typetraits # XXX: this should be removed
var m: Matrix[3, 3, int]
var projectionMatrix: Matrix[4, 4, float]
echo m.transposed.determinant
setPerspectiveProjection projectionMatrix
template ok(x) = assert x
template no(x) = assert(not x)
static:
ok projectionMatrix is AnyTransform3D
no m is AnyTransform3D
type SquareStringMatrix = Matrix[5, 5, string]
ok SquareStringMatrix is AnyMatrix
ok SquareStringMatrix is AnySquareMatrix
no SquareStringMatrix is AnyTransform3D
ok Matrix[5, 10, int] is AnyMatrix
no Matrix[7, 15, float] is AnySquareMatrix
no Matrix[4, 4, int] is AnyTransform3D

View File

@@ -0,0 +1,100 @@
discard """
output: '''true
true
true
true
p has been called.
p has been called.
implicit generic
generic
false
true
-1'''
"""
# https://github.com/nim-lang/Nim/issues/1147
type TTest = object
vals: seq[int]
proc add*(self: var TTest, val: int) =
self.vals.add(val)
type CAddable = concept x
x[].add(int)
echo((ref TTest) is CAddable) # true
# https://github.com/nim-lang/Nim/issues/1570
type ConcretePointOfFloat = object
x, y: float
type ConcretePoint[Value] = object
x, y: Value
type AbstractPointOfFloat = generic p
p.x is float and p.y is float
let p1 = ConcretePointOfFloat(x: 0, y: 0)
let p2 = ConcretePoint[float](x: 0, y: 0)
echo p1 is AbstractPointOfFloat # true
echo p2 is AbstractPointOfFloat # true
echo p2.x is float and p2.y is float # true
# https://github.com/nim-lang/Nim/issues/2018
type ProtocolFollower = generic
true # not a particularly involved protocol
type ImplementorA = object
type ImplementorB = object
proc p[A: ProtocolFollower, B: ProtocolFollower](a: A, b: B) =
echo "p has been called."
p(ImplementorA(), ImplementorA())
p(ImplementorA(), ImplementorB())
# https://github.com/nim-lang/Nim/issues/2423
proc put*[T](c: seq[T], x: T) = echo "generic"
proc put*(c: seq) = echo "implicit generic"
type
Container[T] = concept c
put(c)
put(c, T)
proc c1(x: Container) = echo "implicit generic"
c1(@[1])
proc c2[T](x: Container[T]) = echo "generic"
c2(@[1])
# https://github.com/nim-lang/Nim/issues/2882
type
Paper = object
name: string
Bendable = concept x
bend(x is Bendable)
proc bend(p: Paper): Paper = Paper(name: "bent-" & p.name)
var paper = Paper(name: "red")
echo paper is Bendable
type
A = concept self
size(self) is int
B = object
proc size(self: B): int =
return -1
proc size(self: A): int =
return 0
let b = B()
echo b is A
echo b.size()

View File

@@ -0,0 +1,63 @@
discard """
output: "20\n10"
msg: '''
INFERRED int
VALUE TYPE int
VALUE TYPE NAME INT
IMPLICIT INFERRED int int
IMPLICIT VALUE TYPE int int
IMPLICIT VALUE TYPE NAME INT INT
'''
"""
import typetraits, strutils
template reject(e: expr) =
static: assert(not compiles(e))
type
ArrayStack = object
data: seq[int]
proc push(s: var ArrayStack, item: int) =
s.data.add item
proc pop(s: var ArrayStack): int =
return s.data.pop()
type
Stack[T] = concept var s
s.push(T)
s.pop() is T
type ValueType = T
const ValueTypeName = T.name.toUpper
proc genericAlgorithm[T](s: var Stack[T], y: T) =
static:
echo "INFERRED ", T.name
echo "VALUE TYPE ", s.ValueType.name
echo "VALUE TYPE NAME ", s.ValueTypeName
s.push(y)
echo s.pop
proc implicitGeneric(s: var Stack): auto =
static:
echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
return s.pop()
var s = ArrayStack(data: @[])
s.push 10
s.genericAlgorithm 20
echo s.implicitGeneric
reject s.genericAlgorithm "x"
reject s.genericAlgorithm 1.0
reject "str".implicitGeneric
reject implicitGeneric(10)

View File

@@ -2,20 +2,22 @@ discard """
output: '''Sortable
Sortable
Container
true
true
false
false
false
TObj
int
'''
"""
import typetraits
template reject(expr) = assert(not compiles(x))
type
TObj = object
x: int
JSonValue = object
val: string
Sortable = concept x, y
(x < y) is bool
@@ -23,7 +25,7 @@ type
C.len is Ordinal
for v in items(C):
v.type is tuple|object
proc foo(c: ObjectContainer) =
echo "Container"
@@ -36,33 +38,62 @@ foo(@[TObj(x: 10), TObj(x: 20)])
proc intval(x: int): int = 10
# check real and virtual fields
type
TFoo = concept T
T.x
y(T)
intval T.y
let z = intval(T.y)
TFoo = concept o, type T, ref r, var v, ptr p, static s
o.x
y(o) is int
var str: string
var intref: ref int
refproc(ref T, ref int)
varproc(var T)
ptrproc(ptr T, str)
staticproc(static[T])
typeproc T
T.typeproc
typeproc o.type
o.type.typeproc
o.to(type string)
o.to(type JsonValue)
refproc(r, intref)
varproc(v)
p.ptrproc(string)
staticproc s
typeproc(T)
const TypeName = T.name
type MappedType = type(o.y)
intval y(o)
let z = intval(o.y)
static:
assert T.name.len == 4
reject o.name
reject o.typeproc
reject staticproc(o)
reject o.varproc
reject T.staticproc
reject p.staticproc
proc y(x: TObj): int = 10
proc testFoo(x: TFoo) = discard
proc varproc(x: var TObj) = discard
proc refproc(x: ref TObj, y: ref int) = discard
proc ptrproc(x: ptr TObj, y: string) = discard
proc staticproc(x: static[TObj]) = discard
proc typeproc(t: type TObj) = discard
proc to(x: TObj, t: type string) = discard
proc to(x: TObj, t: type JSonValue) = discard
proc testFoo(x: TFoo) =
echo x.TypeName
echo x.MappedType.name
testFoo(TObj(x: 10))
type
Matrix[Rows, Cols: static[int]; T] = concept M
M.M == Rows
M.N == Cols
M.T is T
MyMatrix[M, N: static[int]; T] = object
data: array[M*N, T]
var x: MyMatrix[3, 3, int]
echo x is Matrix
echo x is Matrix[3, 3, int]
echo x is Matrix[3, 3, float]
echo x is Matrix[4, 3, int]
echo x is Matrix[3, 4, int]

View File

@@ -0,0 +1,15 @@
type VectorSpace[K] = concept x, y
x + y is type(x)
zero(type(x)) is type(x)
-x is type(x)
x - y is type(x)
var k: K
k * x is type(x)
proc zero(T: typedesc): T = 0
static:
assert float is VectorSpace[float]
# assert float is VectorSpace[int]
# assert int is VectorSpace

View File

@@ -0,0 +1,29 @@
discard """
output: '''string
int
(int, string)
'''
"""
import typetraits
type
Foo[T, U] = object
x: T
y: U
proc bar[T](a: T): T.U =
echo result.type.name
proc bas(x: auto): x.T =
echo result.type.name
proc baz(x: Foo): (Foo.T, x.U) =
echo result.type.name
var
f: Foo[int, string]
x = bar f
z = bas f
y = baz f

View File

@@ -1,8 +1,8 @@
discard """
line: 28
nimout: '''tgc_unsafe2.nim(22, 5) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory
tgc_unsafe2.nim(26, 5) Warning: 'track' is not GC-safe as it calls 'trick'
tgc_unsafe2.nim(28, 5) Error: 'consumer' is not GC-safe as it calls 'track'
nimout: '''tgc_unsafe2.nim(22, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2]
tgc_unsafe2.nim(26, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2]
tgc_unsafe2.nim(28, 6) Error: 'consumer' is not GC-safe as it calls 'track'
'''
errormsg: "'consumer' is not GC-safe as it calls 'track'"
"""

View File

@@ -63,6 +63,8 @@ let
var targets = {low(TTarget)..high(TTarget)}
proc normalizeMsg(s: string): string = s.strip.replace("\C\L", "\L")
proc callCompiler(cmdTemplate, filename, options: string,
target: TTarget): TSpec =
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
@@ -184,6 +186,8 @@ proc addResult(r: var TResults, test: TTest,
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
if strip(expected.msg) notin strip(given.msg):
r.addResult(test, expected.msg, given.msg, reMsgsDiffer)
elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg:
r.addResult(test, expected.nimout, given.nimout, reMsgsDiffer)
elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
"internal error:" notin expected.msg:
r.addResult(test, expected.file, given.file, reFilesDiffer)