From 037a1a3e0f2eda7dbcb8034929675fb714649bce Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 23 Aug 2013 21:32:55 +0300 Subject: [PATCH 01/14] parsing of user defined type classes --- compiler/ast.nim | 1 + compiler/parser.nim | 34 +++++++++++++++++++++++++++++++++- lib/core/macros.nim | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index bb06e7163..49e050f6c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -190,6 +190,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 diff --git a/compiler/parser.nim b/compiler/parser.nim index 699a50c62..72b0ba796 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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' @@ -983,6 +984,11 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = else: result = newNodeP(nkObjectTy, p) getTok(p) + of tkGeneric: + if mode == pmTypeDef: + result = parseTypeClass(p) + else: + parMessage(p, errInvalidToken, p.tok) of tkDistinct: if mode == pmTypeDef: result = parseDistinct(p) @@ -1613,6 +1619,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) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 879c66df7..362e670e6 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -59,7 +59,7 @@ type nnkBindStmt, nnkMixinStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, - nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, + nnkTupleTy, nnkTypeClassTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, From 03577bc9367c107597b93922fa14fc6727f4fe50 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 24 Aug 2013 23:44:37 +0300 Subject: [PATCH 02/14] fix parsing of ``proc foo(x: distinct Type)`` --- compiler/parser.nim | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 72b0ba796..ae9ec6105 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -960,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}) @@ -989,12 +990,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result = parseTypeClass(p) else: parMessage(p, errInvalidToken, p.tok) - of tkDistinct: - if mode == pmTypeDef: - result = parseDistinct(p) - else: - result = newNodeP(nkDistinctTy, p) - getTok(p) of tkAddr: result = newNodeP(nkAddr, p) getTokNoInd(p) From 6378fbd66ef9ff85510bf0583610bfc84dc6528f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 25 Aug 2013 12:17:40 +0300 Subject: [PATCH 03/14] working code for simple cases of user-defined type classes --- compiler/ast.nim | 3 +++ compiler/evals.nim | 32 +++------------------- compiler/sem.nim | 6 +++++ compiler/semdata.nim | 6 +++++ compiler/semexprs.nim | 51 ++++++++++++++++++++++++++++------- compiler/semtypes.nim | 16 +++++++++++ compiler/sigmatch.nim | 62 ++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 135 insertions(+), 41 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 49e050f6c..04ac88c1c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -689,6 +689,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 @@ -701,6 +702,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 @@ -1075,6 +1077,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: diff --git a/compiler/evals.nim b/compiler/evals.nim index 053068ea4..7bd74b04a 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -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], {}) diff --git a/compiler/sem.nim b/compiler/sem.nim index 4396a9093..c411d8ac4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -198,6 +198,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) dec(evalTemplateCounter) +proc IsOpImpl(c: PContext, n: PNode): PNode + proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode = markUsed(n, sym) @@ -215,6 +217,9 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, else: result = symNodeFromType(c, e.typ, n.info) + c.evalContext.handleIsOperator = proc (n: PNode): PNode = + result = IsOpImpl(c, n) + result = evalMacroCall(c.evalContext, n, nOrig, sym) if semCheck: result = semAfterMacroCall(c, result, sym) @@ -250,6 +255,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 diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8b04f4af5..4c066f5fa 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -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()) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ebda31501..d80a1cb5a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -295,6 +295,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 +336,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): diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b02fa7c31..e9541997f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5766aa164..25d0d8004 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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, @@ -750,11 +752,55 @@ 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: @@ -776,6 +822,16 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 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) From 89086a8e19292ad986baa808ff42ccad32bf2636 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 25 Aug 2013 18:11:05 +0300 Subject: [PATCH 04/14] prevent eval crashes due to PContext-dependent ops not being available in evalConstExpr --- compiler/evals.nim | 9 +----- compiler/sem.nim | 65 +++++++++++++++++++++++-------------------- compiler/semexprs.nim | 6 ++-- compiler/semstmts.nim | 2 +- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index 7bd74b04a..be6f625ec 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -1440,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 @@ -1450,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] diff --git a/compiler/sem.nim b/compiler/sem.nim index c411d8ac4..71951dd3f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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: @@ -198,8 +216,6 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) dec(evalTemplateCounter) -proc IsOpImpl(c: PContext, n: PNode): PNode - proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode = markUsed(n, sym) @@ -207,18 +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.handleIsOperator = proc (n: PNode): PNode = - result = IsOpImpl(c, n) + c.evalContext = c.createEvalContext(emStatic) result = evalMacroCall(c.evalContext, n, nOrig, sym) if semCheck: result = semAfterMacroCall(c, result, sym) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d80a1cb5a..d0b81364e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -627,18 +627,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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a15b3e10a..3b83290ea 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1144,7 +1144,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 From 28d9398de7f70560254f120ae522e0a4df81576b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 26 Aug 2013 21:53:56 +0300 Subject: [PATCH 05/14] implemented delegators and improved the error messages of unmatched type classes --- compiler/ast.nim | 6 ++- compiler/idents.nim | 1 + compiler/msgs.nim | 2 + compiler/pragmas.nim | 7 +-- compiler/semcall.nim | 111 +++++++++++++++++++++++++++++---------- compiler/semexprs.nim | 31 ++++------- compiler/semmagic.nim | 1 + compiler/semstmts.nim | 9 +++- compiler/semtypes.nim | 2 +- compiler/sigmatch.nim | 22 -------- compiler/types.nim | 1 + compiler/wordrecg.nim | 6 ++- tests/reject/topaque.nim | 24 ++++----- 13 files changed, 130 insertions(+), 93 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 04ac88c1c..b12d58626 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -216,7 +216,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 @@ -353,6 +353,7 @@ type # efficiency nfTransf, # node has been transformed nfSem # node has been checked for semantics + nfDelegate # the call can use a delegator TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) @@ -774,7 +775,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 diff --git a/compiler/idents.nim b/compiler/idents.nim index a50c5269c..f0935c204 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -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" diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3e5304358..47a4d284b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -89,6 +89,7 @@ type errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, + errOnlyACallOpCanBeDelegator, errXExpectsTwoArguments, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, @@ -316,6 +317,7 @@ 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", errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index f185d0f80..aeffcdc4c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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, diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 735e6fac8..22ee8c099 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -34,25 +34,18 @@ 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) @@ -64,6 +57,11 @@ proc resolveOverloads(c: PContext, n, orig: PNode, 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 +72,71 @@ 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 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] + + template pickBest(headSymbol: expr) = + pickBestCandidate(c, headSymbol, n, orig, initialBinding, + filter, result, alt, errors) + + pickBest(f) + + if result.state == csEmpty: + 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 result.state == csEmpty: + 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 +150,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 +207,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)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d0b81364e..369b0eb90 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -682,7 +682,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 @@ -767,11 +767,6 @@ 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) proc buildStringify(c: PContext, arg: PNode): PNode = @@ -979,20 +974,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. @@ -1866,7 +1852,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: @@ -1900,6 +1887,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: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b9ef8b008..88567b10a 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3b83290ea..022d412ce 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -835,7 +835,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) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e9541997f..d8846d717 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 25d0d8004..5411904df 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -202,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 diff --git a/compiler/types.nim b/compiler/types.nim index 2564741d8..99bedfca7 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -442,6 +442,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]) & "]" diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index a9540269c..f8a19ae19 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -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, @@ -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", diff --git a/tests/reject/topaque.nim b/tests/reject/topaque.nim index ac390835b..f0587c959 100644 --- a/tests/reject/topaque.nim +++ b/tests/reject/topaque.nim @@ -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' From bdeaee4a331de99d964386682f49de213b7d7d87 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 26 Aug 2013 23:43:39 +0300 Subject: [PATCH 06/14] fix tnoop This commit requires further investigation. Fixing semExpr(nkCall) once and for all to work with sym-choices and to allow overloading with immediate templates and macros will probably set this straight too. --- compiler/ast.nim | 1 + compiler/semcall.nim | 11 ++++++++--- compiler/semexprs.nim | 8 +++----- tests/reject/tnoop.nim | 11 +++++------ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index b12d58626..4ed44a8fe 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -354,6 +354,7 @@ type 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) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 22ee8c099..643eef065 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -128,9 +128,14 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) if result.state == csEmpty: - errors = @[] - pickBest(f) - NotFoundError(c, n, errors) + if nfExprCall in n.flags: + if c.inCompilesContext > 0 or gErrorCounter == 0: + 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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 369b0eb90..a688efc5b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -732,14 +732,12 @@ 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) #result = afterCallActions(c, result, nOrig, flags) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) diff --git a/tests/reject/tnoop.nim b/tests/reject/tnoop.nim index 2d320c849..c79403e11 100644 --- a/tests/reject/tnoop.nim +++ b/tests/reject/tnoop.nim @@ -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 From dc33638cb256fa47ab723d991e85b746fc690d59 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 27 Aug 2013 00:08:47 +0300 Subject: [PATCH 07/14] proper discrimination between csEmpty and csNoMatch fixes twrongtupleaccess and topaque --- compiler/semcall.nim | 11 ++++++++--- tests/reject/twrongtupleaccess.nim | 4 +--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 643eef065..c7f4e4387 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -50,7 +50,8 @@ proc 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) @@ -115,7 +116,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(f) - if result.state == csEmpty: + let overloadsState = result.state + if overloadsState != csMatch: if nfDelegate in n.flags: InternalAssert f.kind == nkIdent let calleeName = newStrNode(nkStrLit, f.ident.s) @@ -127,7 +129,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) - if result.state == csEmpty: + if overloadsState == csEmpty and result.state == csEmpty: + LocalError(n.info, errUndeclaredIdentifier, considerAcc(f).s) + return + elif result.state != csMatch: if nfExprCall in n.flags: if c.inCompilesContext > 0 or gErrorCounter == 0: LocalError(n.info, errExprXCannotBeCalled, diff --git a/tests/reject/twrongtupleaccess.nim b/tests/reject/twrongtupleaccess.nim index bb09b108d..1a9ae64a2 100644 --- a/tests/reject/twrongtupleaccess.nim +++ b/tests/reject/twrongtupleaccess.nim @@ -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) - - From 9cc23118be5afc701d6eac2f71522aa1f4a327cc Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 27 Aug 2013 01:01:03 +0300 Subject: [PATCH 08/14] fix tcompiles --- compiler/semcall.nim | 7 +++---- compiler/semexprs.nim | 1 + tests/compile/tcompiles.nim | 31 +++++++++++++------------------ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index c7f4e4387..8c20d0faa 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -134,9 +134,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, return elif result.state != csMatch: if nfExprCall in n.flags: - if c.inCompilesContext > 0 or gErrorCounter == 0: - LocalError(n.info, errExprXCannotBeCalled, - renderTree(n, {renderNoComments})) + LocalError(n.info, errExprXCannotBeCalled, + renderTree(n, {renderNoComments})) else: errors = @[] pickBest(f) @@ -217,7 +216,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) + # else: result = errorNode(c, n) proc explicitGenericInstError(n: PNode): PNode = LocalError(n.info, errCannotInstantiateX, renderTree(n)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a688efc5b..aedd2364d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -738,6 +738,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = nOrig.sons[0] = prc n.flags.incl nfExprCall result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) + if result == nil: return errorNode(c, n) #result = afterCallActions(c, result, nOrig, flags) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) diff --git a/tests/compile/tcompiles.nim b/tests/compile/tcompiles.nim index 1a1d947b1..d0fccdaff 100644 --- a/tests/compile/tcompiles.nim +++ b/tests/compile/tcompiles.nim @@ -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" From 52b00630c8a9dd770e3cbc0e1911f73299f5cb57 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 27 Aug 2013 01:49:21 +0300 Subject: [PATCH 09/14] fix tisop Adding nil checks on the exit paths for semCompiles errors. This was probably not needed before, because semIndirectOp had a special detection for semCompiles contexts. We could try to restore these, but wouldn't this just postpone the crashes until someone tries the same expressions in nimrod check? One of the previous commits also used errorNode to avoid returning nil. This may be an alaternative approach. --- compiler/semexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index aedd2364d..5206d88ac 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -766,7 +766,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = let nOrig = n.copyTree #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) - 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 @@ -1994,4 +1994,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) From 01ccb52a00ec4b41b0be3ebb6bafb2a44eba8f26 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 28 Aug 2013 00:21:38 +0300 Subject: [PATCH 10/14] minor improvement towards tgenericshardcases --- compiler/astalgo.nim | 13 ++++++++----- compiler/semexprs.nim | 6 +++++- compiler/semtypinst.nim | 8 ++++++-- compiler/sigmatch.nim | 39 +++++++++++++++++++++++---------------- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index fd6774e7a..6c48dd00f 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -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))) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5206d88ac..b9df29d39 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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 = diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0c15c7248..b638449d2 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -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: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5411904df..ceb8ad156 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -619,7 +619,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) @@ -782,21 +785,25 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 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) @@ -812,7 +819,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, r = typeRel(m, f, a) else: r = typeRel(m, f, a) - + case r of isConvertible: inc(m.convMatches) From b5d833b329191cf99718e2ea6dc08895c47dd8f9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 31 Aug 2013 19:40:36 +0300 Subject: [PATCH 11/14] implemented the using statement --- compiler/ast.nim | 2 ++ compiler/lexer.nim | 4 ++-- compiler/msgs.nim | 3 ++- compiler/parser.nim | 1 + compiler/semcall.nim | 23 ++++++++++++++++++++++- compiler/semexprs.nim | 17 +++++++++++++++++ compiler/wordrecg.nim | 10 +++++----- doc/keywords.txt | 2 +- lib/core/macros.nim | 2 +- 9 files changed, 53 insertions(+), 11 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 4ed44a8fe..6e0cafd74 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 @@ -619,6 +620,7 @@ type TScope* = object depthLevel*: int symbols*: TStrTable + usingSyms*: seq[PNode] parent*: PScope PScope* = ref TScope diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 9e5f4e9d7..1d34c59ab 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -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", diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 47a4d284b..c0b1221ef 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -89,7 +89,7 @@ type errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, - errOnlyACallOpCanBeDelegator, + errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errXExpectsTwoArguments, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, @@ -318,6 +318,7 @@ const 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", diff --git a/compiler/parser.nim b/compiler/parser.nim index ae9ec6105..d19149073 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1775,6 +1775,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 = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8c20d0faa..b2f41d509 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -96,6 +96,11 @@ proc NotFoundError*(c: PContext, n: PNode, errors: seq[string]) = 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 @@ -109,11 +114,27 @@ proc resolveOverloads(c: PContext, n, orig: PNode, 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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b9df29d39..30b28a35d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1329,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) @@ -1967,6 +1983,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) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f8a19ae19..b37a7bb4f 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -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, @@ -72,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, @@ -95,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] = ["", @@ -107,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", @@ -154,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", diff --git a/doc/keywords.txt b/doc/keywords.txt index 2a775cd94..fa3ce4786 100644 --- a/doc/keywords.txt +++ b/doc/keywords.txt @@ -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 diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 362e670e6..9ac6b0082 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -56,7 +56,7 @@ type nnkFromStmt, nnkIncludeStmt, - nnkBindStmt, nnkMixinStmt, + nnkBindStmt, nnkMixinStmt, nnkUsingStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTypeClassTy, nnkRecList, nnkRecCase, nnkRecWhen, From c8c8d2035af189bdf63b39d4e47266a6e67c38b9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 3 Sep 2013 03:14:22 +0300 Subject: [PATCH 12/14] partially documented the new features --- doc/manual.txt | 130 +++++++++++++++++++++++++++++++++++++++++++++++-- web/news.txt | 5 ++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 0a2009961..db309c4eb 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/web/news.txt b/web/news.txt index 896cf9941..d863b2ed8 100644 --- a/web/news.txt +++ b/web/news.txt @@ -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! From 6a04757e6d14b5b59004ef03539dba4453352fe5 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 3 Sep 2013 03:18:09 +0300 Subject: [PATCH 13/14] switching the compiler to the markAndSweep collector This produces faster bootstrapping, works properly for the caas mode and fixes the Aporia build on a mac --- compiler/nimrod.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/nimrod.cfg b/compiler/nimrod.cfg index a0e59aa09..fbebe1cec 100644 --- a/compiler/nimrod.cfg +++ b/compiler/nimrod.cfg @@ -2,6 +2,8 @@ mainModule:"nimrod.nim" +gc:markAndSweep + hint[XDeclaredButNotUsed]:off path:"llvm" path:"$projectPath/.." From 73aba23a49c3fd6b14cc7b2688224d8ac68a1acf Mon Sep 17 00:00:00 2001 From: Mark Flamer Date: Mon, 2 Sep 2013 18:20:00 -0700 Subject: [PATCH 14/14] Add arity typetrait --- compiler/evals.nim | 4 ++++ lib/pure/typetraits.nim | 2 ++ 2 files changed, 6 insertions(+) diff --git a/compiler/evals.nim b/compiler/evals.nim index be6f625ec..f8336fce7 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -914,6 +914,10 @@ proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = result = newStrNode(nkStrLit, typ.typeToString(preferName)) result.typ = newType(tyString, context) result.info = trait.info + of "arity": + result = newIntNode(nkIntLit, typ.n.len-1) + result.typ = newType(tyInt, context) + result.info = trait.info else: internalAssert false diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 45b79dcb6..e7bd363cf 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -13,3 +13,5 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} ## Returns the name of the given type +proc arity*(t: typedesc): int {.magic: "TypeTrait".} + ## Returns the arity of the given type \ No newline at end of file