implemented delegators and improved the error messages of unmatched type classes

This commit is contained in:
Zahary Karadjov 2013-08-26 21:53:56 +03:00
commit 28d9398de7
13 changed files with 130 additions and 93 deletions

View file

@ -216,7 +216,7 @@ type
TNodeKinds* = set[TNodeKind] TNodeKinds* = set[TNodeKind]
type type
TSymFlag* = enum # already 30 flags! TSymFlag* = enum # already 32 flags!
sfUsed, # read access of sym (for warnings) or simply used sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed sfFromGeneric, # symbol is instantiation of a generic; this is needed
@ -353,6 +353,7 @@ type
# efficiency # efficiency
nfTransf, # node has been transformed nfTransf, # node has been transformed
nfSem # node has been checked for semantics nfSem # node has been checked for semantics
nfDelegate # the call can use a delegator
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23)
@ -774,7 +775,8 @@ const
tyProc, tyString, tyError} tyProc, tyString, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfAllConst, nfDelegate}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2

View file

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

View file

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

View file

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

View file

@ -34,25 +34,18 @@ proc sameMethodDispatcher(a, b: PSym): bool =
proc determineType(c: PContext, s: PSym) proc determineType(c: PContext, s: PSym)
proc resolveOverloads(c: PContext, n, orig: PNode, proc
filter: TSymKinds): TCandidate = pickBestCandidate(c: PContext, headSymbol: PNode,
var initialBinding: PNode n, orig: PNode,
var f = n.sons[0] initialBinding: PNode,
if f.kind == nkBracketExpr: filter: TSymKinds,
# fill in the bindings: best, alt: var TCandidate,
initialBinding = f errors: var seq[string]) =
f = f.sons[0] var o: TOverloadIter
else: var sym = initOverloadIter(o, c, headSymbol)
initialBinding = nil
var
o: TOverloadIter
alt, z: TCandidate
template best: expr = result
#Message(n.info, warnUser, renderTree(n))
var sym = initOverloadIter(o, c, f)
var symScope = o.lastOverloadScope var symScope = o.lastOverloadScope
var z: TCandidate
if sym == nil: return if sym == nil: return
initCandidate(best, sym, initialBinding, symScope) initCandidate(best, sym, initialBinding, symScope)
@ -64,6 +57,11 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
initCandidate(z, sym, initialBinding, o.lastOverloadScope) initCandidate(z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym z.calleeSym = sym
matches(c, n, orig, z) 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: if z.state == csMatch:
# little hack so that iterators are preferred over everything else: # little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200) 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 if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far elif cmp == 0: alt = z # x is as good as the best so far
else: nil else: nil
sym = nextOverloadIter(o, c, f) sym = nextOverloadIter(o, c, headSymbol)
if best.state == csEmpty: proc NotFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# no overloaded proc found # Gives a detailed error message; this is separated from semOverloadedCall,
# do not generate an error yet; the semantic checking will check for # as semOverlodedCall is already pretty slow (and we need this information
# an overloaded () operator # only in case of an error).
elif alt.state == csMatch and cmpCandidates(best, alt) == 0 and if c.InCompilesContext > 0:
not sameMethodDispatcher(best.calleeSym, alt.calleeSym): # fail fast:
if best.state != csMatch: GlobalError(n.info, errTypeMismatch, "")
InternalError(n.info, "x.state is not csMatch") var result = msgKindToString(errTypeMismatch)
#writeMatches(best) 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) #writeMatches(alt)
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# quick error message for performance of 'compiles' built-in: # quick error message for performance of 'compiles' built-in:
@ -98,7 +150,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
add(args, ")") add(args, ")")
LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [ LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(best.calleeSym), getProcHeader(alt.calleeSym), getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
args]) args])
@ -155,6 +207,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode = filter: TSymKinds): PNode =
var r = resolveOverloads(c, n, nOrig, filter) var r = resolveOverloads(c, n, nOrig, filter)
if r.state == csMatch: result = semResolvedCall(c, n, r) if r.state == csMatch: result = semResolvedCall(c, n, r)
else: result = errorNode(c, n)
proc explicitGenericInstError(n: PNode): PNode = proc explicitGenericInstError(n: PNode): PNode =
LocalError(n.info, errCannotInstantiateX, renderTree(n)) LocalError(n.info, errCannotInstantiateX, renderTree(n))

View file

@ -682,7 +682,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = n.sons[0] result = n.sons[0]
result.kind = nkCall result.kind = nkCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags) return semDirectOp(c, result, flags)
else: else:
n.sons[0] = semExpr(c, n.sons[0]) n.sons[0] = semExpr(c, n.sons[0])
let nOrig = n.copyTree let nOrig = n.copyTree
@ -767,11 +767,6 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
let nOrig = n.copyTree let nOrig = n.copyTree
#semLazyOpAux(c, n) #semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) 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) result = afterCallActions(c, result, nOrig, flags)
proc buildStringify(c: PContext, arg: PNode): PNode = proc buildStringify(c: PContext, arg: PNode): PNode =
@ -979,20 +974,11 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
addSon(result, copyTree(n[0])) addSon(result, copyTree(n[0]))
else: else:
var i = considerAcc(n.sons[1]) var i = considerAcc(n.sons[1])
var f = searchInScopes(c, i) result = newNodeI(nkDotCall, n.info)
# if f != nil and f.kind == skStub: loadStub(f) result.flags.incl nfDelegate
# ``loadStub`` is not correct here as we don't care for ``f`` really addSon(result, newIdentNode(i, n[1].info))
if f != nil: addSon(result, copyTree(n[0]))
# 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)
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts # this is difficult, because the '.' is used in many different contexts
# in Nimrod. We first allow types in the semantic checking. # 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: of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) 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: if s != nil:
case s.kind case s.kind
of skMacro: 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 elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and
isSymChoice(n[0][0]): isSymChoice(n[0][0]):
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
elif nfDelegate in n.flags:
result = semDirectOp(c, n, flags)
else: else:
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)
of nkWhen: of nkWhen:

View file

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

View file

@ -835,7 +835,14 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
var it = n.sons[i] var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
let m = lookupMacro(c, key) 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 # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it: # let the semantic checker deal with it:
var x = newNodeI(nkCall, n.info) var x = newNodeI(nkCall, n.info)

View file

@ -608,7 +608,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if genericParams.sons[i].sym.name.id == finalTypId.id: if genericParams.sons[i].sym.name.id == finalTypId.id:
return genericParams.sons[i].typ 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) if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass) s.linkTo(typeClass)
s.position = genericParams.len s.position = genericParams.len

View file

@ -202,28 +202,6 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
add(result, argTypeToString(arg)) add(result, argTypeToString(arg))
if i != sonsLen(n) - 1: add(result, ", ") 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 typeRel(c: var TCandidate, f, a: PType): TTypeRelation
proc concreteType(c: TCandidate, t: PType): PType = proc concreteType(c: TCandidate, t: PType): PType =
case t.kind case t.kind

View file

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

View file

@ -39,7 +39,8 @@ type
wDestroy, wDestroy,
wImmediate, wDestructor, wImportCpp, wImportObjC, wImmediate, wDestructor, wDelegator,
wImportCpp, wImportObjC,
wImportCompilerProc, wImportCompilerProc,
wImportc, wExportc, wIncompleteStruct, wRequiresInit, wImportc, wExportc, wIncompleteStruct, wRequiresInit,
wAlign, wNodecl, wPure, wSideeffect, wHeader, wAlign, wNodecl, wPure, wSideeffect, wHeader,
@ -120,7 +121,8 @@ const
"destroy", "destroy",
"immediate", "destructor", "importcpp", "importobjc", "immediate", "destructor", "delegator",
"importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "incompletestruct", "importcompilerproc", "importc", "exportc", "incompletestruct",
"requiresinit", "align", "nodecl", "pure", "sideeffect", "requiresinit", "align", "nodecl", "pure", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",

View file

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