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
parent 89086a8e19
commit 28d9398de7
13 changed files with 130 additions and 93 deletions

View File

@@ -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

View File

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

View File

@@ -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",

View File

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

View File

@@ -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))

View File

@@ -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:

View File

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

View File

@@ -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)

View File

@@ -608,7 +608,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if genericParams.sons[i].sym.name.id == finalTypId.id:
return genericParams.sons[i].typ
var s = newSym(skType, finalTypId, getCurrOwner(), info)
var s = newSym(skType, finalTypId, typeClass.sym, info)
if typId == nil: s.flags.incl(sfAnon)
s.linkTo(typeClass)
s.position = genericParams.len

View File

@@ -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

View File

@@ -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]) & "]"

View File

@@ -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",

View File

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