Merge branch 'type-classes' into upstream

This commit is contained in:
Zahary Karadjov
2013-09-03 03:14:56 +03:00
27 changed files with 594 additions and 249 deletions

View File

@@ -178,6 +178,7 @@ type
nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement
nkMixinStmt, # a mixin statement
nkUsingStmt, # an using statement
nkCommentStmt, # a comment statement
nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates
@@ -190,6 +191,7 @@ type
nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body
nkTupleTy, # tuple body
nkTypeClassTy, # user-defined type class
nkRecList, # list of object parts
nkRecCase, # case section of object
nkRecWhen, # when section of object
@@ -215,7 +217,7 @@ type
TNodeKinds* = set[TNodeKind]
type
TSymFlag* = enum # already 30 flags!
TSymFlag* = enum # already 32 flags!
sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed
@@ -352,6 +354,8 @@ type
# efficiency
nfTransf, # node has been transformed
nfSem # node has been checked for semantics
nfDelegate # the call can use a delegator
nfExprCall # this is an attempt to call a regular expression
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23)
@@ -616,6 +620,7 @@ type
TScope* = object
depthLevel*: int
symbols*: TStrTable
usingSyms*: seq[PNode]
parent*: PScope
PScope* = ref TScope
@@ -688,6 +693,7 @@ type
# for enum types a list of symbols
# for tyInt it can be the int literal
# for procs and tyGenericBody, it's the
# the body of the user-defined type class
# formal param list
# else: unused
destructor*: PSym # destructor. warning: nil here may not necessary
@@ -700,6 +706,7 @@ type
# -1 means that the size is unkwown
align*: int # the type's alignment requirements
loc*: TLoc
testeeName*: PIdent # the test variable in user-defined type classes
TPair*{.final.} = object
key*, val*: PObject
@@ -771,7 +778,8 @@ const
tyProc, tyString, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfAllConst, nfDelegate}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -1074,6 +1082,7 @@ proc assignType(dest, src: PType) =
dest.size = src.size
dest.align = src.align
dest.destructor = src.destructor
dest.testeeName = src.testeeName
# this fixes 'type TLock = TSysLock':
if src.sym != nil:
if dest.sym != nil:

View File

@@ -430,11 +430,14 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)])
proc debug(n: PSym) =
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [
toRope(n.name.s), toRope(n.id), flagsToStr(n.flags),
flagsToStr(n.loc.flags)])))
proc debug(n: PSym) =
if n == nil:
writeln(stdout, "null")
else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [
toRope(n.name.s), toRope(n.id), flagsToStr(n.flags),
flagsToStr(n.loc.flags)])))
proc debug(n: PType) =
writeln(stdout, ropeToStr(debugType(n)))

View File

@@ -53,7 +53,8 @@ type
features: TSandboxFlags
globals*: TIdNodeTable # state of global vars
getType*: proc(n: PNode): PNode {.closure.}
handleIsOperator*: proc(n: PNode): PNode {.closure.}
PEvalContext* = ref TEvalContext
TEvalFlag = enum
@@ -916,33 +917,6 @@ proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
else:
internalAssert false
proc evalIsOp*(n: PNode): PNode =
InternalAssert n.sonsLen == 3 and
n[1].kind == nkSym and n[1].sym.kind == skType and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].sym.typ
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags))
of "iterator":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator in t.flags))
else:
let t2 = n[2].typ
var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1)
else: sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc expectString(n: PNode) =
if n.kind notin nkStrKinds:
GlobalError(n.info, errStringLiteralExpected)
@@ -1038,7 +1012,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
result = evalTypeTrait(n[0], operand, c.module)
of mIs:
n.sons[1] = evalAux(c, n.sons[1], {})
result = evalIsOp(n)
result = c.handleIsOperator(n)
of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
of mStaticExec:
let cmd = evalAux(c, n.sons[1], {})
@@ -1466,8 +1440,7 @@ proc eval*(c: PEvalContext, n: PNode): PNode =
else:
stackTrace(c, result.info, errCannotInterpretNodeX, renderTree(n))
proc evalConstExprAux(module, prc: PSym, e: PNode, mode: TEvalMode): PNode =
var p = newEvalContext(module, mode)
proc evalConstExprAux*(p: PEvalContext, module, prc: PSym, e: PNode): PNode =
var s = newStackFrame()
s.call = e
s.prc = prc
@@ -1476,12 +1449,6 @@ proc evalConstExprAux(module, prc: PSym, e: PNode, mode: TEvalMode): PNode =
if result != nil and result.kind == nkExceptBranch: result = nil
popStackFrame(p)
proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst)
proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode =
result = evalConstExprAux(module, prc, e, emStatic)
proc setupMacroParam(x: PNode): PNode =
result = x
if result.kind == nkHiddenStdConv: result = result.sons[1]

View File

@@ -103,4 +103,5 @@ proc IdentEq*(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id
var idAnon* = getIdent":anonymous"
let idDelegator* = getIdent":delegator"

View File

@@ -41,7 +41,7 @@ type
tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface,
tkIs, tkIsnot, tkIterator,
tkLambda, tkLet,
tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin,
tkMacro, tkMethod, tkMixin, tkUsing, tkMod, tkNil, tkNot, tkNotin,
tkObject, tkOf, tkOr, tkOut,
tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShared, tkShl, tkShr, tkStatic,
tkTemplate,
@@ -75,7 +75,7 @@ const
"finally", "for", "from", "generic", "if",
"import", "in", "include", "interface", "is", "isnot", "iterator",
"lambda", "let",
"macro", "method", "mixin", "mod",
"macro", "method", "mixin", "using", "mod",
"nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return",
"shared", "shl", "shr", "static",

View File

@@ -92,6 +92,7 @@ type
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
@@ -319,6 +320,8 @@ const
errCannotRenderX: "cannot render reStructuredText element \'$1\'",
errVarVarTypeNotAllowed: "type \'var var\' is not allowed",
errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errXExpectsTwoArguments: "\'$1\' expects two arguments",
errXExpectsObjectTypes: "\'$1\' expects object types",
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",

View File

@@ -927,9 +927,10 @@ proc parseExpr(p: var TParser): PNode =
of tkTry: result = parseTry(p)
else: result = simpleExpr(p)
proc parseEnum(p: var TParser): PNode
proc parseObject(p: var TParser): PNode
proc parseDistinct(p: var TParser): PNode
proc parseEnum(p: var TParser): PNode
proc parseTypeClass(p: var TParser): PNode
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
#| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple'
@@ -959,6 +960,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkShared: result = parseTypeDescKAux(p, nkSharedTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr, mode)
of tkTuple: result = parseTuple(p, mode == pmTypeDef)
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
@@ -983,12 +985,11 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
else:
result = newNodeP(nkObjectTy, p)
getTok(p)
of tkDistinct:
of tkGeneric:
if mode == pmTypeDef:
result = parseDistinct(p)
result = parseTypeClass(p)
else:
result = newNodeP(nkDistinctTy, p)
getTok(p)
parMessage(p, errInvalidToken, p.tok)
of tkAddr:
result = newNodeP(nkAddr, p)
getTokNoInd(p)
@@ -1612,6 +1613,32 @@ proc parseObject(p: var TParser): PNode =
return
addSon(result, parseObjectPart(p))
proc parseTypeClass(p: var TParser): PNode =
result = newNodeP(nkTypeClassTy, p)
getTok(p)
addSon(result, p.parseSymbol)
if p.tok.tokType == tkCurlyDotLe and p.validInd:
addSon(result, parsePragma(p))
else:
addSon(result, ast.emptyNode)
if p.tok.tokType == tkOf and p.tok.indent < 0:
var a = newNodeP(nkOfInherit, p)
getTok(p)
while true:
addSon(a, parseTypeDesc(p))
if p.tok.tokType != tkComma: break
getTok(p)
addSon(result, a)
else:
addSon(result, ast.emptyNode)
if p.tok.tokType == tkComment:
skipComment(p, result)
# an initial IND{>} HAS to follow:
if not realInd(p):
addSon(result, emptyNode)
else:
addSon(result, parseStmt(p))
proc parseDistinct(p: var TParser): PNode =
#| distinct = 'distinct' optInd typeDesc
result = newNodeP(nkDistinctTy, p)
@@ -1747,6 +1774,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p, nkBindStmt)
of tkMixin: result = parseBind(p, nkMixinStmt)
of tkUsing: result = parseBind(p, nkUsingStmt)
else: result = simpleStmt(p)
proc parseStmt(p: var TParser): PNode =

View File

@@ -24,13 +24,14 @@ const
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGenSym, wInject, wRaises, wTags, wOperator}
wGenSym, wInject, wRaises, wTags, wOperator, wDelegator}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject, wDirty}
templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject, wDirty,
wDelegator}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject, wDelegator}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject, wRaises,

View File

@@ -132,14 +132,42 @@ proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
LocalError(typ.n.info, errXisNoType, typeToString(typ))
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
semCheck: bool = true): PNode
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 semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
semCheck: bool = true): PNode
proc symFromType(t: PType, info: TLineInfo): PSym =
if t.sym != nil: return t.sym
result = newSym(skType, getIdent"AnonType", t.owner, info)
result.flags.incl sfAnon
result.typ = t
proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
result = newSymNode(symFromType(t, info), info)
result.typ = makeTypeDesc(c, t)
proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
result = newEvalContext(c.module, mode)
result.getType = proc (n: PNode): PNode =
var e = tryExpr(c, n)
if e == nil:
result = symNodeFromType(c, errorType(c), n.info)
elif e.typ == nil:
result = newSymNode(getSysSym"void")
else:
result = symNodeFromType(c, e.typ, n.info)
result.handleIsOperator = proc (n: PNode): PNode =
result = IsOpImpl(c, n)
proc evalConstExpr(c: PContext, module: PSym, e: PNode): PNode =
result = evalConstExprAux(c.createEvalContext(emConst), module, nil, e)
proc evalStaticExpr(c: PContext, module: PSym, e: PNode, prc: PSym): PNode =
result = evalConstExprAux(c.createEvalContext(emStatic), module, prc, e)
proc semConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
@@ -148,7 +176,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return n
result = getConstExpr(c.module, e)
if result == nil:
result = evalConstExpr(c.module, e)
result = evalConstExpr(c, c.module, e)
if result == nil or result.kind == nkEmpty:
if e.info != n.info:
pushInfoContext(n.info)
@@ -161,16 +189,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
include hlo, seminst, semcall
proc symFromType(t: PType, info: TLineInfo): PSym =
if t.sym != nil: return t.sym
result = newSym(skType, getIdent"AnonType", t.owner, info)
result.flags.incl sfAnon
result.typ = t
proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
result = newSymNode(symFromType(t, info), info)
result.typ = makeTypeDesc(c, t)
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
@@ -205,15 +223,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
GlobalError(n.info, errRecursiveDependencyX, sym.name.s)
if c.evalContext == nil:
c.evalContext = newEvalContext(c.module, emStatic)
c.evalContext.getType = proc (n: PNode): PNode =
var e = tryExpr(c, n)
if e == nil:
result = symNodeFromType(c, errorType(c), n.info)
elif e.typ == nil:
result = newSymNode(getSysSym"void")
else:
result = symNodeFromType(c, e.typ, n.info)
c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.evalContext, n, nOrig, sym)
if semCheck: result = semAfterMacroCall(c, result, sym)
@@ -250,6 +260,7 @@ proc myOpen(module: PSym): PPassContext =
if c.p != nil: InternalError(module.info, "sem.myOpen")
c.semConstExpr = semConstExpr
c.semExpr = semExpr
c.semTryExpr = tryExpr
c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall

View File

@@ -34,36 +34,35 @@ proc sameMethodDispatcher(a, b: PSym): bool =
proc determineType(c: PContext, s: PSym)
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds): TCandidate =
var initialBinding: PNode
var f = n.sons[0]
if f.kind == nkBracketExpr:
# fill in the bindings:
initialBinding = f
f = f.sons[0]
else:
initialBinding = nil
var
o: TOverloadIter
alt, z: TCandidate
template best: expr = result
#Message(n.info, warnUser, renderTree(n))
var sym = initOverloadIter(o, c, f)
proc
pickBestCandidate(c: PContext, headSymbol: PNode,
n, orig: PNode,
initialBinding: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
errors: var seq[string]) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var symScope = o.lastOverloadScope
var z: TCandidate
if sym == nil: return
initCandidate(best, sym, initialBinding, symScope)
initCandidate(alt, sym, initialBinding, symScope)
best.state = csNoMatch
while sym != nil:
if sym.kind in filter:
determineType(c, sym)
initCandidate(z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd(getProcHeader(sym))
if z.errors != nil:
for err in z.errors:
errors[errors.len - 1].add("\n " & err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200)
@@ -74,17 +73,100 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
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
else: nil
sym = nextOverloadIter(o, c, f)
sym = nextOverloadIter(o, c, headSymbol)
if best.state == csEmpty:
# no overloaded proc found
# do not generate an error yet; the semantic checking will check for
# an overloaded () operator
elif alt.state == csMatch and cmpCandidates(best, alt) == 0 and
not sameMethodDispatcher(best.calleeSym, alt.calleeSym):
if best.state != csMatch:
InternalError(n.info, "x.state is not csMatch")
#writeMatches(best)
proc NotFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# 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.InCompilesContext > 0:
# fail fast:
GlobalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags)))
add(result, ')')
var candidates = ""
for err in errors:
add(candidates, err)
add(candidates, "\n")
if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
LocalError(n.Info, errGenerated, result)
proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
for scope in walkScopes(c.currentScope):
if scope.usingSyms != nil:
for s in scope.usingSyms: usedSyms.safeAdd(s)
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds): TCandidate =
var initialBinding: PNode
var alt: TCandidate
var f = n.sons[0]
if f.kind == nkBracketExpr:
# fill in the bindings:
initialBinding = f
f = f.sons[0]
else:
initialBinding = nil
var errors: seq[string]
var usedSyms: seq[PNode]
template pickBest(headSymbol: expr) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors)
gatherUsedSyms(c, usedSyms)
if usedSyms != nil:
var hiddenArg = if usedSyms.len > 1: newNode(nkClosedSymChoice, n.info, usedSyms)
else: usedSyms[0]
n.sons.insert(hiddenArg, 1)
orig.sons.insert(hiddenArg, 1)
pickBest(f)
if result.state != csMatch:
n.sons.delete(1)
orig.sons.delete(1)
else: return
pickBest(f)
let overloadsState = result.state
if overloadsState != csMatch:
if nfDelegate in n.flags:
InternalAssert f.kind == nkIdent
let calleeName = newStrNode(nkStrLit, f.ident.s)
calleeName.info = n.info
let callOp = newIdentNode(idDelegator, n.info)
n.sons[0..0] = [callOp, calleeName]
orig.sons[0..0] = [callOp, calleeName]
pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty:
LocalError(n.info, errUndeclaredIdentifier, considerAcc(f).s)
return
elif result.state != csMatch:
if nfExprCall in n.flags:
LocalError(n.info, errExprXCannotBeCalled,
renderTree(n, {renderNoComments}))
else:
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.inCompilesContext > 0:
# quick error message for performance of 'compiles' built-in:
@@ -98,7 +180,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
add(args, ")")
LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(best.calleeSym), getProcHeader(alt.calleeSym),
getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
args])
@@ -155,6 +237,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode =
var r = resolveOverloads(c, n, nOrig, filter)
if r.state == csMatch: result = semResolvedCall(c, n, r)
# else: result = errorNode(c, n)
proc explicitGenericInstError(n: PNode): PNode =
LocalError(n.info, errCannotInstantiateX, renderTree(n))

View File

@@ -72,6 +72,7 @@ type
libs*: TLinkedList # 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.}
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,
@@ -204,6 +205,11 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
result = newTypeS(tyTypeDesc, c)
result.addSonSkipIntLit(typ.AssertNotNil)
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info)
proc newTypeS(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner())

View File

@@ -248,7 +248,11 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
# do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar)
else: LocalError(n.info, errInvalidArgForX, opToStr[m])
of tyGenericParam:
# leave it for now, it will be resolved in semtypinst
n.typ = getSysType(tyInt)
else:
LocalError(n.info, errInvalidArgForX, opToStr[m])
result = n
proc semSizeof(c: PContext, n: PNode): PNode =
@@ -295,6 +299,39 @@ proc semOf(c: PContext, n: PNode): PNode =
n.typ = getSysType(tyBool)
result = n
proc IsOpImpl(c: PContext, n: PNode): PNode =
InternalAssert n.sonsLen == 3 and
n[1].kind == nkSym and n[1].sym.kind == skType and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].sym.typ.skipTypes({tyTypeDesc})
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags))
of "iterator":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator in t.flags))
else:
var match: bool
let t2 = n[2].typ
if t2.kind == tyTypeClass:
var m: TCandidate
InitCandidate(m, t2)
match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil
else:
match = sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc semIs(c: PContext, n: PNode): PNode =
if sonsLen(n) != 3:
LocalError(n.info, errXExpectsTwoArguments, "is")
@@ -303,21 +340,21 @@ proc semIs(c: PContext, n: PNode): PNode =
n.typ = getSysType(tyBool)
n.sons[1] = semExprWithType(c, n[1], {efDetermineType})
if n[1].typ.kind != tyTypeDesc:
LocalError(n[0].info, errTypeExpected)
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
if n[1].typ.sonsLen == 0:
if n[1].typ.kind != tyTypeDesc:
n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info)
elif n[1].typ.sonsLen == 0:
# this is a typedesc variable, leave for evals
return
else:
let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(t1): result = evalIsOp(n)
let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(t1): result = IsOpImpl(c, n)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType}
for i in countup(1, n.sonsLen-1):
@@ -594,18 +631,18 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
call.add(a)
#echo "NOW evaluating at compile time: ", call.renderTree
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, call, c.p.owner)
result = evalStaticExpr(c, c.module, call, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(call))
else:
result = evalConstExpr(c.module, call)
result = evalConstExpr(c, c.module, call)
if result.isNil: result = n
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode): PNode =
let a = semExpr(c, n.sons[0])
result = evalStaticExpr(c.module, a, c.p.owner)
result = evalStaticExpr(c, c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
@@ -649,7 +686,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = n.sons[0]
result.kind = nkCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags)
return semDirectOp(c, result, flags)
else:
n.sons[0] = semExpr(c, n.sons[0])
let nOrig = n.copyTree
@@ -699,14 +736,13 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# Now that nkSym does not imply an iteration over the proc/iterator space,
# the old ``prc`` (which is likely an nkIdent) has to be restored:
if result == nil:
# XXX: hmm, what kind of symbols will end up here?
# do we really need to try the overload resolution?
n.sons[0] = prc
nOrig.sons[0] = prc
n.flags.incl nfExprCall
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil:
if c.inCompilesContext > 0 or gErrorCounter == 0:
LocalError(n.info, errExprXCannotBeCalled,
renderTree(n, {renderNoComments}))
return errorNode(c, n)
if result == nil: return errorNode(c, n)
#result = afterCallActions(c, result, nOrig, flags)
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
@@ -734,12 +770,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil:
result = overloadedCallOpr(c, n)
if result == nil:
NotFoundError(c, n)
return errorNode(c, n)
result = afterCallActions(c, result, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags)
proc buildStringify(c: PContext, arg: PNode): PNode =
if arg.typ != nil and
@@ -946,20 +977,11 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
addSon(result, copyTree(n[0]))
else:
var i = considerAcc(n.sons[1])
var f = searchInScopes(c, i)
# if f != nil and f.kind == skStub: loadStub(f)
# ``loadStub`` is not correct here as we don't care for ``f`` really
if f != nil:
# BUGFIX: do not check for (f.kind in {skProc, skMethod, skIterator}) here
# This special node kind is to merge with the call handler in `semExpr`.
result = newNodeI(nkDotCall, n.info)
addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0]))
else:
if not ContainsOrIncl(c.UnknownIdents, i.id):
LocalError(n.Info, errUndeclaredFieldX, i.s)
result = errorNode(c, n)
result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDelegate
addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0]))
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts
# in Nimrod. We first allow types in the semantic checking.
@@ -1307,6 +1329,22 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo,
result = newSym(kind, idAnon, owner, info)
result.flags = {sfGenSym}
proc semUsing(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
for e in n.sons:
let usedSym = semExpr(c, e)
if usedSym.kind == nkSym:
case usedSym.sym.kind
of skLocalVars + {skConst}:
c.currentScope.usingSyms.safeAdd(usedSym)
continue
of skProcKinds:
addDeclAt(c.currentScope, usedSym.sym)
continue
else: nil
LocalError(e.info, errUsingNoSymbol, e.renderTree)
proc semExpandToAst(c: PContext, n: PNode): PNode =
var macroCall = n[1]
var expandedSym = expectMacroOrTemplateCall(c, macroCall)
@@ -1834,7 +1872,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
let mode = if nfDelegate in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookup(c, n.sons[0], mode)
if s != nil:
case s.kind
of skMacro:
@@ -1868,6 +1907,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and
isSymChoice(n[0][0]):
result = semDirectOp(c, n, flags)
elif nfDelegate in n.flags:
result = semDirectOp(c, n, flags)
else:
result = semIndirectOp(c, n, flags)
of nkWhen:
@@ -1943,6 +1984,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkForStmt, nkParForStmt: result = semFor(c, n)
of nkCaseStmt: result = semCase(c, n)
of nkReturnStmt: result = semReturn(c, n)
of nkUsingStmt: result = semUsing(c, n)
of nkAsmStmt: result = semAsm(c, n)
of nkYieldStmt: result = semYield(c, n)
of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
@@ -1974,4 +2016,4 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
else:
LocalError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments}))
incl(result.flags, nfSem)
if result != nil: incl(result.flags, nfSem)

View File

@@ -113,6 +113,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result.typ = getSysType(tyString)
of mInstantiationInfo: result = semInstantiationInfo(c, n)
of mOrd: result = semOrd(c, n)
of mHigh: result = semLowHigh(c, n, mHigh)
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n)
of mLocals: result = semLocals(c, n)

View File

@@ -838,7 +838,14 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
let m = lookupMacro(c, key)
if m == nil: continue
if m == nil:
if key.kind == nkIdent and key.ident.id == ord(wDelegator):
if considerAcc(prc.sons[namePos]).s == "()":
prc.sons[namePos] = newIdentNode(idDelegator, prc.info)
prc.sons[pragmasPos] = copyExcept(n, i)
else:
LocalError(prc.info, errOnlyACallOpCanBeDelegator)
continue
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it:
var x = newNodeI(nkCall, n.info)
@@ -1147,7 +1154,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
proc semStaticStmt(c: PContext, n: PNode): PNode =
let a = semStmt(c, n.sons[0])
result = evalStaticExpr(c.module, a, c.p.owner)
result = evalStaticExpr(c, c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode

View File

@@ -608,7 +608,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if genericParams.sons[i].sym.name.id == finalTypId.id:
return genericParams.sons[i].typ
var s = newSym(skType, finalTypId, getCurrOwner(), info)
var s = newSym(skType, finalTypId, typeClass.sym, info)
if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass)
s.position = genericParams.len
@@ -871,6 +871,21 @@ proc freshType(res, prev: PType): PType {.inline.} =
else:
result = res
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
result = newOrPrevType(tyTypeClass, prev, c)
result.testeeName = considerAcc(n[0])
result.n = n[3]
let
pragmas = n[1]
inherited = n[2]
if inherited.kind != nkEmpty:
for n in inherited.sons:
let typ = semTypeNode(c, n, nil)
result.sons.safeAdd(typ)
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -973,6 +988,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev)
of nkTupleTy: result = semTuple(c, n, prev)
of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarType(c, n, prev)

View File

@@ -148,12 +148,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if result != nil: return
for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i]
if x.kind == tyGenericParam:
if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x)
if header == nil: header = copyType(t, t.owner, false)
header.sons[i] = x
propagateToOwner(header, x)
#idTablePut(cl.typeMap, body.sons[i-1], x)
#idTablePut(cl.typeMap, body.sons[i-1], x)
if header != nil:
# search again after first pass:
result = searchInstTypes(header)
@@ -200,6 +201,9 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = lookupTypeVar(cl, t)
if result.kind == tyGenericInvokation:
result = handleGenericInvokation(cl, result)
of tyExpr:
if t.sym != nil and t.sym.kind == skGenericParam:
result = lookupTypeVar(cl, t)
of tyGenericInvokation:
result = handleGenericInvokation(cl, t)
of tyGenericBody:

View File

@@ -40,7 +40,9 @@ type
# be instantiated
typedescMatched: bool
inheritancePenalty: int # to prefer closest father object type
errors*: seq[string] # additional clarifications to be displayed to the
# user if overload resolution fails
TTypeRelation* = enum # order is important!
isNone, isConvertible,
isIntConv,
@@ -200,28 +202,6 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
add(result, argTypeToString(arg))
if i != sonsLen(n) - 1: add(result, ", ")
proc NotFoundError*(c: PContext, n: PNode) =
# 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.InCompilesContext > 0:
# fail fast:
GlobalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n))
add(result, ')')
var candidates = ""
var o: TOverloadIter
var sym = initOverloadIter(o, c, n.sons[0])
while sym != nil:
if sym.kind in RoutineKinds:
add(candidates, getProcHeader(sym))
add(candidates, "\n")
sym = nextOverloadIter(o, c, n.sons[0])
if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
LocalError(n.Info, errGenerated, result)
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation
proc concreteType(c: TCandidate, t: PType): PType =
case t.kind
@@ -643,7 +623,10 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
else:
result = isNone
else:
result = matchTypeClass(c, f, a)
if a.kind == tyTypeClass:
result = isGeneric
else:
result = matchTypeClass(c, f, a)
if result == isGeneric:
var concrete = concreteType(c, a)
@@ -754,35 +737,93 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
arg: PNode, f, a: PType): PNode =
if f.n == nil:
let r = typeRel(m, f, a)
return if r == isGeneric: arg else: nil
var prev = PType(idTableGet(m.bindings, f))
if prev != nil:
if sameType(prev, a): return arg
else: return nil
# pushInfoContext(arg.info)
openScope(c)
var testee = newSym(skParam, f.testeeName, f.sym, f.sym.info)
testee.typ = a
addDecl(c, testee)
for stmt in f.n:
var e = c.semTryExpr(c, copyTree(stmt))
if e == nil:
let expStr = renderTree(stmt, {renderNoComments})
m.errors.safeAdd("can't compile " & expStr & " for " & a.typeToString)
return nil
case e.kind
of nkReturnStmt:
nil
of nkTypeSection: nil
of nkConstDef: nil
else:
if e.typ.kind == tyBool:
let verdict = c.semConstExpr(c, e)
if verdict.intVal == 0:
let expStr = renderTree(stmt, {renderNoComments})
m.errors.safeAdd(expStr & " doesn't hold for " & a.typeToString)
return nil
closeScope(c)
result = arg
put(m.bindings, f, a)
proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode =
argSemantized, argOrig: PNode): PNode =
var arg = argSemantized
var r: TTypeRelation
let fMaybeExpr = f.skipTypes({tyDistinct})
if fMaybeExpr.kind == tyExpr:
case fMaybeExpr.kind
of tyExpr:
if fMaybeExpr.sonsLen == 0:
r = isGeneric
else:
let match = matchTypeClass(m, fMaybeExpr, a)
if match != isGeneric: r = isNone
if a.kind == tyExpr:
InternalAssert a.len > 0
r = typeRel(m, f.lastSon, a.lastSon)
else:
# XXX: Ideally, this should happen much earlier somewhere near
# semOpAux, but to do that, we need to be able to query the
# overload set to determine whether compile-time value is expected
# for the param before entering the full-blown sigmatch algorithm.
# This is related to the immediate pragma since querying the
# overload set could help there too.
var evaluated = c.semConstExpr(c, arg)
if evaluated != nil:
r = isGeneric
arg.typ = newTypeS(tyExpr, c)
arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated
let match = matchTypeClass(m, fMaybeExpr, a)
if match != isGeneric: r = isNone
else:
# XXX: Ideally, this should happen much earlier somewhere near
# semOpAux, but to do that, we need to be able to query the
# overload set to determine whether compile-time value is expected
# for the param before entering the full-blown sigmatch algorithm.
# This is related to the immediate pragma since querying the
# overload set could help there too.
var evaluated = c.semConstExpr(c, arg)
if evaluated != nil:
r = isGeneric
arg.typ = newTypeS(tyExpr, c)
arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated
if r == isGeneric:
put(m.bindings, f, arg.typ)
of tyTypeClass:
if fMaybeExpr.n != nil:
let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a)
if match != nil:
r = isGeneric
arg = match
else:
r = isNone
else:
r = typeRel(m, f, a)
else:
r = typeRel(m, f, a)
case r
of isConvertible:
inc(m.convMatches)

View File

@@ -445,6 +445,7 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if t.len == 0: result = "typedesc"
else: result = "typedesc[" & constraintsToStr(t) & "]"
of tyTypeClass:
if t.n != nil: return t.sym.owner.name.s
case t.len
of 0: result = "typeclass[]"
of 1: result = "typeclass[" & consToStr(t.sons[0]) & "]"

View File

@@ -28,7 +28,7 @@ type
wElif, wElse, wEnd, wEnum, wExcept, wExport,
wFinally, wFor, wFrom, wGeneric, wIf, wImport, wIn,
wInclude, wInterface, wIs, wIsnot, wIterator, wLambda, wLet,
wMacro, wMethod, wMixin, wMod, wNil,
wMacro, wMethod, wMixin, wUsing, wMod, wNil,
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
wShared, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wVar,
wWhen, wWhile, wWith, wWithout, wXor, wYield,
@@ -39,7 +39,8 @@ type
wDestroy,
wImmediate, wDestructor, wImportCpp, wImportObjC,
wImmediate, wDestructor, wDelegator,
wImportCpp, wImportObjC,
wImportCompilerProc,
wImportc, wExportc, wIncompleteStruct, wRequiresInit,
wAlign, wNodecl, wPure, wSideeffect, wHeader,
@@ -71,7 +72,7 @@ type
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
wUnion, wUnsigned, wUsing, wVirtual, wVoid, wVolatile, wWchar_t,
wUnion, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
@@ -94,7 +95,7 @@ const
cppNimSharedKeywords* = {
wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile}
wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing }
specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["",
@@ -106,7 +107,7 @@ const
"finally", "for", "from", "generic", "if",
"import", "in", "include", "interface", "is", "isnot", "iterator",
"lambda", "let",
"macro", "method", "mixin", "mod", "nil", "not", "notin",
"macro", "method", "mixin", "using", "mod", "nil", "not", "notin",
"object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return",
"shared", "shl", "shr", "static",
@@ -120,7 +121,8 @@ const
"destroy",
"immediate", "destructor", "importcpp", "importobjc",
"immediate", "destructor", "delegator",
"importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "incompletestruct",
"requiresinit", "align", "nodecl", "pure", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
@@ -152,7 +154,7 @@ const
"private", "protected", "public", "register", "reinterpret_cast",
"short", "signed", "sizeof", "static_cast", "struct", "switch",
"this", "throw", "true", "typedef", "typeid",
"typename", "union", "unsigned", "using", "virtual", "void", "volatile",
"typename", "union", "unsigned", "virtual", "void", "volatile",
"wchar_t",
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",

View File

@@ -7,7 +7,7 @@ finally for from
generic
if import in include interface is isnot iterator
lambda let
macro method mixin mod
macro method mixin using mod
nil not notin
object of or out
proc ptr

View File

@@ -2166,6 +2166,39 @@ specified in the statement's pragmas. The default special character is ``'`'``:
theEnd:
"""
Using statement
---------------
The using statement provides syntactic convenience for procs that heavily use a
single contextual parameter. When applied to a variable or a constant, it will
instruct Nimrod to automatically consider the used symbol as a hidden leading
parameter for any procedure calls, following the using statement in the current
scope. Thus, it behaves much like the hidden `this` parameter available in some
object-oriented programming languages.
.. code-block:: nimrod
var s = socket()
using s
connect(host, port)
send(data)
while true:
let line = readLine(timeout)
...
When applied to a callable symbol, it brings the designated symbol in the
current scope. Thus, it can be used to disambiguate between imported symbols
from different modules having the same name.
.. code-block:: nimrod
import windows, sdl
using sdl.SetTimer
If expression
-------------
@@ -3191,14 +3224,69 @@ the dot syntax:
proc `[]`(m: TMatrix, row, col: int): TMatrix.T =
m.data[col * high(TMatrix.Columns) + row]
If anonymous type classes are used, the ``type`` operator can be used to
discover the instantiated type of each param.
Alternatively, the `type` operator can be used over the proc params for similar
effect when anonymous or distinct type classes are used.
When a generic type is instantiated with a type class instead of a concrete
type, this results in another more specific type class:
.. code-block:: nimrod
seq[ref object] # Any sequence storing references to any object type
type T1 = auto
proc foo(s: seq[T1], e: T1)
# seq[T1] is the same as just `seq`, but T1 will be allowed to bind
# to a single type, while the signature is being matched
TMatrix[Ordinal] # Any TMatrix instantiation using integer values
As seen in the previous example, in such instantiations, it's not necessary to
supply all type parameters of the generic type, because any missing ones will
be inferred to have the equivalent of the `any` type class and thus they will
match anything without discrimination.
User defined type classes
-------------------------
To be written.
The user-defined type classes are available in two flavours - declarative and
imperative. Both are used to specify an arbitrary set of requirements that the
matched type must satisfy.
Declarative type classes are written in the following form:
.. code-block:: nimrod
type
Comparable = generic x, y
(x < y) is bool
Container[T] = generic c
c.len is ordinal
items(c) is iterator
for value in c:
type(value) is T
The identifiers following the `generic` keyword are treated as variables of
the matched type and the body of the type class consists of arbitrary code that
must be valid under these circumstances.
Specifically, the type class will be matched 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
Please note that the `is` operator allows you 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 doesn't reveal implementation details.
.. code-block:: nimrod
Much like generics, the user defined type classes will be instantiated exactly
once for each tested type and any static code included within them will also be
executed once.
Return Type Inference
@@ -3910,7 +3998,7 @@ the ordinary AST predicates:
template ex{a = b + c}(a: int{noalias}, b, c: int) =
# this transformation is only valid if 'b' and 'c' do not alias 'a':
a = b
inc a, b
inc a, c
Pattern operators
@@ -4412,6 +4500,40 @@ be destructed at its scope exit. Later versions of the language will improve
the support of destructors.
delegator pragma
----------------
The delegator pragma can be used to intercept and rewrite proc call and field
access attempts referring to previously undeclared symbol names. It can be used
to provide a fluent interface to objects lying outside the static confines of
the Nimrod's type system such as values from dynamic scripting languages or
dynamic file formats such as JSON or XML.
A delegator is a special form of the `()` operator marked with the delagator
pragma. When Nimrod encounters an expression that cannot be resolved by the
standard overload resolution, any delegators in the current scope will be
matched against a rewritten form of the expression following the standard
signature matching rules. In the rewritten expression, the name of the unknown
proc or field name is inserted as an additional static string parameter always
appearing in the leading position:
.. code-block:: nimrod
a.b => delegator("b", a)
a.b(c, d) => delegator("b", a, c)
a b, c, d => delegator("a", b, c, d)
The delegators can be any callable symbol type (procs, templates, macros)
depending on the desired effect:
.. code-block:: nimrod
proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field]
var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
echo js.y # outputs 2
procvar pragma
--------------
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a

View File

@@ -56,10 +56,10 @@ type
nnkFromStmt,
nnkIncludeStmt,
nnkBindStmt, nnkMixinStmt,
nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkTupleTy, nnkTypeClassTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy,
nnkDistinctTy,

View File

@@ -1,31 +1,26 @@
discard """
output: '''obj has '==': false
int has '==': true
false
true
true
no'''
"""
# test the new 'compiles' feature:
template supports(opr, x: expr): bool {.immediate.} =
compiles(opr(x)) or compiles(opr(x, x))
template ok(x: expr): stmt =
static: assert(x)
template no(x: expr): stmt =
static: assert(not x)
type
TObj = object
var
myObj {.compileTime.}: TObj
echo "obj has '==': ", supports(`==`, myObj)
echo "int has '==': ", supports(`==`, 45)
ok supports(`==`, myObj)
ok supports(`==`, 45)
echo supports(`++`, 34)
echo supports(`not`, true)
echo supports(`+`, 34)
no supports(`++`, 34)
ok supports(`not`, true)
ok supports(`+`, 34)
no compiles(4+5.0 * "hallo")
when compiles(4+5.0 * "hallo"):
echo "yes"
else:
echo "no"

View File

@@ -3,11 +3,10 @@ discard """
line: 11
errormsg: "expression \'a()\' cannot be called"
"""
# Tests the new check in the semantic pass
var
a: int
a() #ERROR_MSG expression 'a()' cannot be called
# Tests the new check in the semantic pass
var
a: int
a() #ERROR_MSG expression 'a()' cannot be called

View File

@@ -1,18 +1,18 @@
discard """
file: "topaque.nim"
line: 16
errormsg: "undeclared field: \'buffer\'"
errormsg: "undeclared identifier: \'buffer\'"
"""
# Test the new opaque types
import
mopaque
var
L: TLexer
L.filename = "ha"
L.line = 34
L.buffer[0] = '\0' #ERROR_MSG undeclared field: 'buffer'
# Test the new opaque types
import
mopaque
var
L: TLexer
L.filename = "ha"
L.line = 34
L.buffer[0] = '\0' #ERROR_MSG undeclared field: 'buffer'

View File

@@ -1,12 +1,10 @@
discard """
file: "twrongtupleaccess.nim"
line: 9
errormsg: "undeclared field: \'setBLAH\'"
errormsg: "undeclared identifier: \'setBLAH\'"
"""
# Bugfix
var v = (5.0, 10.0)
v.setBLAH(10)

View File

@@ -49,6 +49,11 @@ Language Additions
- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
- Added ``requiresInit`` pragma to enforce explicit initialization.
- Added ``using statement`` for better authoring domain-specific languages and
OOP-like syntactic sugar.
- Added ``delegator pragma`` for handling calls to missing procs and fields at
compile-time.
- Support for user-defined type classes have been added.
2013-05-20 New website design!