mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 13:30:33 +00:00
Merge remote-tracking branch 'origin/concepts-rebased' into araq
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)")
|
||||
|
||||
@@ -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 & ')')
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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] == '#'):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
15
tests/concepts/matrix.nim
Normal 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
|
||||
|
||||
28
tests/concepts/matrixalgo.nim
Normal file
28
tests/concepts/matrixalgo.nim
Normal 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
21
tests/concepts/t1128.nim
Normal 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
41
tests/concepts/t3330.nim
Normal 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
32
tests/concepts/t976.nim
Normal 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)
|
||||
|
||||
69
tests/concepts/tconcepts_overload_precedence.nim
Normal file
69
tests/concepts/tconcepts_overload_precedence.nim
Normal 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
120
tests/concepts/texplain.nim
Normal 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)
|
||||
|
||||
102
tests/concepts/tmapconcept.nim
Normal file
102
tests/concepts/tmapconcept.nim
Normal 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]
|
||||
|
||||
81
tests/concepts/tmatrixconcept.nim
Normal file
81
tests/concepts/tmatrixconcept.nim
Normal 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
|
||||
|
||||
31
tests/concepts/tmatrixlib.nim
Normal file
31
tests/concepts/tmatrixlib.nim
Normal 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
|
||||
|
||||
100
tests/concepts/tmisc_issues.nim
Normal file
100
tests/concepts/tmisc_issues.nim
Normal 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()
|
||||
|
||||
63
tests/concepts/tstackconcept.nim
Normal file
63
tests/concepts/tstackconcept.nim
Normal 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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
15
tests/concepts/tvectorspace.nim
Normal file
15
tests/concepts/tvectorspace.nim
Normal 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
|
||||
|
||||
29
tests/generics/tgenericdotrettype.nim
Normal file
29
tests/generics/tgenericdotrettype.nim
Normal 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
|
||||
|
||||
@@ -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'"
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user