diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2016-02-28 03:17:20 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2016-02-28 03:17:20 +0100 |
commit | 1afdefcbe930b042e5f81b30b960c67bed1e859b (patch) | |
tree | 79ef15191d0c821585ddcc15903dd953a01a15f5 /compiler | |
parent | 6d2fd0c9f17016ee9ae5f8c337445ff958cba2f0 (diff) | |
download | Nim-1afdefcbe930b042e5f81b30b960c67bed1e859b.tar.gz |
added experimental .this pragma
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 1 | ||||
-rw-r--r-- | compiler/astalgo.nim | 5 | ||||
-rw-r--r-- | compiler/pragmas.nim | 7 | ||||
-rw-r--r-- | compiler/semcall.nim | 40 | ||||
-rw-r--r-- | compiler/semdata.nim | 13 | ||||
-rw-r--r-- | compiler/semexprs.nim | 192 | ||||
-rw-r--r-- | compiler/seminst.nim | 44 | ||||
-rw-r--r-- | compiler/semstmts.nim | 5 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 4 |
9 files changed, 193 insertions, 118 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 2ce0afcc3..a7fb7c7e9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -298,6 +298,7 @@ const sfWrittenTo* = sfBorrow # param is assigned to sfEscapes* = sfProcvar # param escapes sfBase* = sfDiscriminant + sfIsSelf* = sfOverriden # param is 'self' const # getting ready for the future expr/stmt merge diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1a70875d4..4772009b4 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -635,7 +635,7 @@ proc reallySameIdent(a, b: string): bool {.inline.} = else: result = true -proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = +proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} = # returns true if n is already in the string table: # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! @@ -654,7 +654,8 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = replaceSlot = h h = nextTry(h, high(t.data)) if replaceSlot >= 0: - t.data[replaceSlot] = n # overwrite it with newer definition! + if not onConflictKeepOld: + t.data[replaceSlot] = n # overwrite it with newer definition! return true # found it elif mustRehash(len(t.data), t.counter): strTableEnlarge(t) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index f10d552a1..d4996981d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -46,7 +46,7 @@ const wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, - wInjectStmt, wDeprecated, wExperimental} + wInjectStmt, wDeprecated, wExperimental, wThis} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, @@ -875,6 +875,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, c.module.flags.incl sfExperimental else: localError(it.info, "'experimental' pragma only valid as toplevel statement") + of wThis: + if it.kind == nkExprColonExpr: + c.selfName = considerQuotedIdent(it[1]) + else: + c.selfName = getIdent("self") of wNoRewrite: noVal(it) of wBase: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index eba1059ef..51cc8bfb0 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -39,7 +39,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, initialBinding: PNode, filter: TSymKinds, best, alt: var TCandidate, - errors: var CandidateErrors) = + errors: var CandidateErrors; + fromUsingStmt: bool) = var o: TOverloadIter # thanks to the lazy semchecking for operands, we need to iterate over the # symbol table *before* any call to 'initCandidate' which might invoke @@ -50,7 +51,12 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var syms: seq[tuple[a: PSym, b: int]] = @[] while symx != nil: - if symx.kind in filter: syms.add((symx, o.lastOverloadScope)) + if symx.kind in filter: + if fromUsingStmt and (symx.typ.n.len <= 1 or + sfIsSelf notin symx.typ.n[1].sym.flags): + discard "only consider procs that have a 'self' too" + else: + syms.add((symx, o.lastOverloadScope)) symx = nextOverloadIter(o, c, headSymbol) if syms.len == 0: return @@ -63,7 +69,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, let sym = syms[i][0] determineType(c, sym) initCandidate(c, z, sym, initialBinding, syms[i][1]) - z.calleeSym = sym #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: # gDebug = true @@ -156,28 +161,27 @@ proc resolveOverloads(c: PContext, n, orig: PNode, else: initialBinding = nil - var usedSyms: seq[PNode] - - template pickBest(headSymbol: expr) = + template pickBest(headSymbol, fromUsingStmt) = 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] - + filter, result, alt, errors, fromUsingStmt) + + #gatherUsedSyms(c, usedSyms) + if c.p != nil and c.p.selfSym != nil: + # we need to enforce semchecking of selfSym again because it + # might need auto-deref: + var hiddenArg = newSymNode(c.p.selfSym) + hiddenArg.typ = nil n.sons.insert(hiddenArg, 1) orig.sons.insert(hiddenArg, 1) - pickBest(f) + pickBest(f, true) if result.state != csMatch: n.sons.delete(1) orig.sons.delete(1) else: return - pickBest(f) + pickBest(f, false) let overloadsState = result.state if overloadsState != csMatch: @@ -194,7 +198,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let op = newIdentNode(getIdent(x), n.info) n.sons[0] = op orig.sons[0] = op - pickBest(op) + pickBest(op, false) if nfExplicitCall in n.flags: tryOp ".()" @@ -209,7 +213,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let callOp = newIdentNode(getIdent".=", n.info) n.sons[0..1] = [callOp, n[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName] - pickBest(callOp) + pickBest(callOp, false) if overloadsState == csEmpty and result.state == csEmpty: if nfDotField in n.flags and nfExplicitCall notin n.flags: @@ -228,7 +232,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, n.sons[0] = f errors = @[] - pickBest(f) + pickBest(f, false) #notFoundError(c, n, errors) return diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 656bfc449..48d1a1a74 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -30,6 +30,7 @@ type # statements owner*: PSym # the symbol this context belongs to resultSym*: PSym # the result symbol (if we are in a proc) + selfSym*: PSym # the 'self' symbol (if available) nestedLoopCounter*: int # whether we are in a loop or not nestedBlockCounter*: int # whether we are in a block or not inTryStmt*: int # whether we are in a try statement; works also @@ -103,7 +104,7 @@ type inParallelStmt*: int instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp): PSym {.nimcall.} - + selfName*: PIdent proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s @@ -154,16 +155,6 @@ proc popOwner() = proc lastOptionEntry(c: PContext): POptionEntry = result = POptionEntry(c.optionStack.tail) -proc pushProcCon*(c: PContext, owner: PSym) {.inline.} = - if owner == nil: - internalError("owner is nil") - return - var x: PProcCon - new(x) - x.owner = owner - x.next = c.p - c.p = x - proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc newOptionEntry(): POptionEntry = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 87d7764a2..6009644fa 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -77,88 +77,6 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = result.typ = s.typ result.info = n.info -proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = - case s.kind - of skConst: - markUsed(n.info, s) - styleCheckUse(n.info, s) - case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, - tyTuple, tySet, tyUInt..tyUInt64: - if s.magic == mNone: result = inlineConst(n, s) - else: result = newSymNode(s, n.info) - of tyArrayConstr, tySequence: - # Consider:: - # const x = [] - # proc p(a: openarray[int]) - # proc q(a: openarray[char]) - # p(x) - # q(x) - # - # It is clear that ``[]`` means two totally different things. Thus, we - # copy `x`'s AST into each context, so that the type fixup phase can - # deal with two different ``[]``. - if s.ast.len == 0: result = inlineConst(n, s) - else: result = newSymNode(s, n.info) - else: - result = newSymNode(s, n.info) - of skMacro: result = semMacroExpr(c, n, n, s, flags) - of skTemplate: result = semTemplateExpr(c, n, s, flags) - of skParam: - markUsed(n.info, s) - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: - # XXX see the hack in sigmatch.nim ... - return s.typ.n - elif sfGenSym in s.flags: - if c.p.wasForwarded: - # gensym'ed parameters that nevertheless have been forward declared - # need a special fixup: - let realParam = c.p.owner.typ.n[s.position+1] - internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam - return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) - elif c.p.owner.kind == skMacro: - # gensym'ed macro parameters need a similar hack (see bug #1944): - var u = searchInScopes(c, s.name) - internalAssert u != nil and u.kind == skParam and u.owner == s.owner - return newSymNode(u, n.info) - result = newSymNode(s, n.info) - of skVar, skLet, skResult, skForVar: - if s.magic == mNimvm: - localError(n.info, "illegal context for 'nimvm' magic") - - markUsed(n.info, s) - styleCheckUse(n.info, s) - # if a proc accesses a global variable, it is not side effect free: - if sfGlobal in s.flags: - incl(c.p.owner.flags, sfSideEffect) - result = newSymNode(s, n.info) - # We cannot check for access to outer vars for example because it's still - # not sure the symbol really ends up being used: - # var len = 0 # but won't be called - # genericThatUsesLen(x) # marked as taking a closure? - of skGenericParam: - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic: - result = newSymNode(s, n.info) - result.typ = s.typ - elif s.ast != nil: - result = semExpr(c, s.ast) - else: - n.typ = s.typ - return n - of skType: - markUsed(n.info, s) - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: - return s.typ.n - result = newSymNode(s, n.info) - result.typ = makeTypeDesc(c, s.typ) - else: - markUsed(n.info, s) - styleCheckUse(n.info, s) - result = newSymNode(s, n.info) - type TConvStatus = enum convOK, @@ -1015,6 +933,116 @@ proc readTypeParameter(c: PContext, typ: PType, return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) #echo "came here: returned nil" +proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + case s.kind + of skConst: + markUsed(n.info, s) + styleCheckUse(n.info, s) + case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind + of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + tyTuple, tySet, tyUInt..tyUInt64: + if s.magic == mNone: result = inlineConst(n, s) + else: result = newSymNode(s, n.info) + of tyArrayConstr, tySequence: + # Consider:: + # const x = [] + # proc p(a: openarray[int]) + # proc q(a: openarray[char]) + # p(x) + # q(x) + # + # It is clear that ``[]`` means two totally different things. Thus, we + # copy `x`'s AST into each context, so that the type fixup phase can + # deal with two different ``[]``. + if s.ast.len == 0: result = inlineConst(n, s) + else: result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + of skMacro: result = semMacroExpr(c, n, n, s, flags) + of skTemplate: result = semTemplateExpr(c, n, s, flags) + of skParam: + markUsed(n.info, s) + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + # XXX see the hack in sigmatch.nim ... + return s.typ.n + elif sfGenSym in s.flags: + if c.p.wasForwarded: + # gensym'ed parameters that nevertheless have been forward declared + # need a special fixup: + let realParam = c.p.owner.typ.n[s.position+1] + internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam + return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) + elif c.p.owner.kind == skMacro: + # gensym'ed macro parameters need a similar hack (see bug #1944): + var u = searchInScopes(c, s.name) + internalAssert u != nil and u.kind == skParam and u.owner == s.owner + return newSymNode(u, n.info) + result = newSymNode(s, n.info) + of skVar, skLet, skResult, skForVar: + if s.magic == mNimvm: + localError(n.info, "illegal context for 'nimvm' magic") + + markUsed(n.info, s) + styleCheckUse(n.info, s) + # if a proc accesses a global variable, it is not side effect free: + if sfGlobal in s.flags: + incl(c.p.owner.flags, sfSideEffect) + result = newSymNode(s, n.info) + # We cannot check for access to outer vars for example because it's still + # not sure the symbol really ends up being used: + # var len = 0 # but won't be called + # genericThatUsesLen(x) # marked as taking a closure? + of skGenericParam: + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic: + result = newSymNode(s, n.info) + result.typ = s.typ + elif s.ast != nil: + result = semExpr(c, s.ast) + else: + n.typ = s.typ + return n + of skType: + markUsed(n.info, s) + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + return s.typ.n + result = newSymNode(s, n.info) + result.typ = makeTypeDesc(c, s.typ) + of skField: + if c.p != nil and c.p.selfSym != nil: + var ty = skipTypes(c.p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + var check: PNode = nil + if ty.kind == tyObject: + while true: + check = nil + let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check) + if f != nil and fieldVisible(c, f): + # is the access to a public field or in the same module or in a friend? + doAssert f == s + markUsed(n.info, f) + styleCheckUse(n.info, f) + result = newNodeIT(nkDotExpr, n.info, f.typ) + result.add makeDeref(newSymNode(c.p.selfSym)) + result.add newSymNode(f) # we now have the correct field + if check != nil: + check.sons[0] = result + check.typ = result.typ + result = check + return result + if ty.sons[0] == nil: break + ty = skipTypes(ty.sons[0], {tyGenericInst}) + # old code, not sure if it's live code: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + else: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access checkSonsLen(n, 2) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 2c767ffc6..4a45dee9d 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,6 +10,47 @@ # This module implements the instantiation of generic procs. # included from sem.nim +proc addObjFieldsToLocalScope(c: PContext; n: PNode) = + template rec(n) = addObjFieldsToLocalScope(c, n) + case n.kind + of nkRecList: + for i in countup(0, len(n)-1): + rec n[i] + of nkRecCase: + if n.len > 0: rec n.sons[0] + for i in countup(1, len(n)-1): + if n[i].kind in {nkOfBranch, nkElse}: rec lastSon(n[i]) + of nkSym: + let f = n.sym + if f.kind == skField and fieldVisible(c, f): + c.currentScope.symbols.strTableIncl(f, onConflictKeepOld=true) + incl(f.flags, sfUsed) + # it is not an error to shadow fields via parameters + else: discard + +proc rawPushProcCon(c: PContext, owner: PSym) = + var x: PProcCon + new(x) + x.owner = owner + x.next = c.p + c.p = x + +proc rawHandleSelf(c: PContext; owner: PSym) = + if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil: + let params = owner.typ.n + if params.len > 1: + let arg = params[1].sym + if arg.name.id == c.selfName.id: + c.p.selfSym = arg + arg.flags.incl sfIsSelf + let t = c.p.selfSym.typ.skipTypes(abstractPtrs) + if t.kind == tyObject: + addObjFieldsToLocalScope(c, t.n) + +proc pushProcCon*(c: PContext; owner: PSym) = + rawPushProcCon(c, owner) + rawHandleSelf(c, owner) + iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = internalAssert n.kind == nkGenericParams for i, a in n.pairs: @@ -248,7 +289,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, addDecl(c, s) entry.concreteTypes[i] = s.typ inc i - pushProcCon(c, result) + rawPushProcCon(c, result) instantiateProcType(c, pt, result, info) for j in 1 .. result.typ.len-1: entry.concreteTypes[i] = result.typ.sons[j] @@ -263,6 +304,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). #if c.compilesContextId == 0: + rawHandleSelf(c, result) entry.compilesId = c.compilesContextId fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index edcf079fa..d1c088d73 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1206,9 +1206,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # Macros and Templates can have generic parameters, but they are # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) + pushProcCon(c, s) if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if not usePseudoGenerics: paramsTypeCheck(c, s.typ) - pushProcCon(c, s) + c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) if sfImportc notin s.flags: @@ -1217,7 +1218,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': n.sons[bodyPos] = transformBody(c.module, semBody, s) - popProcCon(c) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) @@ -1228,6 +1228,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: n.sons[bodyPos] = ast.emptyNode + popProcCon(c) else: if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 96df0c5c6..ceadc2fea 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -37,6 +37,7 @@ type # is this a top-level symbol or a nested proc? call*: PNode # modified call bindings*: TIdTable # maps types to types + magic*: TMagic # magic of operation baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example fauxMatch*: TTypeKind # the match was successful only due to the use @@ -114,6 +115,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, c.calleeScope = 1 else: c.calleeScope = calleeScope + c.magic = c.calleeSym.magic initIdTable(c.bindings) c.errors = nil if binding != nil and callee.kind in routineKinds: @@ -1691,7 +1693,7 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = matchesAux(c, n, nOrig, m, marker) proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = - if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}: + if m.magic in {mArrGet, mArrPut}: m.state = csMatch m.call = n return |