requested code review changes

This commit is contained in:
Zahary Karadjov
2017-03-29 16:15:30 +03:00
parent 01207b6cfd
commit a74ad869e9
6 changed files with 66 additions and 49 deletions

View File

@@ -99,7 +99,10 @@ proc pickBestCandidate(c: PContext, headSymbol: 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
elif errors != nil or z.diagnostics != nil:
errors.safeAdd((sym, int z.mutabilityProblem, z.diagnostics))
errors.safeAdd(CandidateError(
sym: sym,
unmatchedVarParam: int z.mutabilityProblem,
diagnostics: z.diagnostics))
else:
# Symbol table has been modified. Restart and pre-calculate all syms
# before any further candidate init and compare. SLOW, but rare case.
@@ -126,9 +129,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
# we do a pre-analysis. If all types produce the same string, we will add
# module information.
let proto = describeArgs(c, n, 1, preferName)
for err, mut, diagnostics in items(errors):
for err in errors:
var errProto = ""
let n = err.typ.n
let n = err.sym.typ.n
for i in countup(1, n.len - 1):
var p = n.sons[i]
if p.kind == nkSym:
@@ -140,16 +143,17 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
break
var candidates = ""
for err, mut, diagnostics in items(errors):
if err.kind in routineKinds and err.ast != nil:
add(candidates, renderTree(err.ast,
for err in errors:
if err.sym.kind in routineKinds and err.sym.ast != nil:
add(candidates, renderTree(err.sym.ast,
{renderNoBody, renderNoComments, renderNoPragmas}))
else:
add(candidates, err.getProcHeader(prefer))
add(candidates, err.sym.getProcHeader(prefer))
add(candidates, "\n")
if mut != 0 and mut < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
for diag in diagnostics:
if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" &
renderTree(n[err.unmatchedVarParam]) & "' is immutable\n")
for diag in err.diagnostics:
add(candidates, diag & "\n")
result = (prefer, candidates)
@@ -180,7 +184,9 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
errors.add((symx, 0, nil))
errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0,
diagnostics: nil))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(n.info, "could not resolve: " & $n)
@@ -405,7 +411,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors)
else:
if efExplain notin flags:
if efExplain notin flags and c.compilesContextId == 0:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})

View File

@@ -1555,9 +1555,14 @@ proc usesResult(n: PNode): bool =
for c in n:
if usesResult(c): return true
proc inferConceptStaticParam(c: PContext, typ: PType, n: PNode) =
proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
var typ = inferred.typ
let res = semConstExpr(c, n)
if not sameType(res.typ, typ.base): localError(n.info, "")
if not sameType(res.typ, typ.base):
localError(n.info,
"cannot infer the concept parameter '%s', due to a type mismatch. " &
"attempt to equate '%s' and '%s'.",
[inferred.renderTree, $res.typ, $typ.base])
typ.n = res
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
@@ -1616,12 +1621,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if c.inTypeClass > 0 and expr.typ != nil:
case expr.typ.kind
of tyBool:
if expr.kind == nkInfix and expr[0].sym.name.s == "==":
if expr.kind == nkInfix and
expr[0].kind == nkSym and
expr[0].sym.name.s == "==":
if expr[1].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[1].typ, expr[2])
inferConceptStaticParam(c, expr[1], expr[2])
continue
elif expr[2].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[2].typ, expr[1])
inferConceptStaticParam(c, expr[2], expr[1])
continue
let verdict = semConstExpr(c, n[i])

View File

@@ -1235,7 +1235,7 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
if n.kind == nkType:
result = symFromType(n.typ, n.info)
else:
localError(n.info, "xx")
localError(n.info, errTypeExpected)
result = errorSym(c, n)
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =

View File

@@ -22,10 +22,10 @@ type
TCandidateState* = enum
csEmpty, csMatch, csNoMatch
CandidateError = tuple
sym: PSym
unmatchedVarParam: int
diagnostics: seq[string]
CandidateError* = object
sym*: PSym
unmatchedVarParam*: int
diagnostics*: seq[string]
CandidateErrors* = seq[CandidateError]
@@ -589,8 +589,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): PType =
var
Concept = ff.skipTypes({tyUserTypeClassInst})
body = Concept.n[3]
typeClass = ff.skipTypes({tyUserTypeClassInst})
body = typeClass.n[3]
if c.inTypeClass > 4:
localError(body.info, $body & " too nested for type matching")
return nil
@@ -611,7 +611,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
param: PSym
template paramSym(kind): untyped =
newSym(kind, typeParamName, Concept.sym, Concept.sym.info)
newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
block addTypeParam:
for prev in typeParams:
@@ -642,7 +642,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
addDecl(c, param)
for param in Concept.n[0]:
for param in typeClass.n[0]:
var
dummyName: PNode
dummyType: PType
@@ -665,7 +665,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, Concept.sym, Concept.sym.info)
dummyName.ident, typeClass.sym, typeClass.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
@@ -675,7 +675,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
errorPrefix: string
flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or
sfExplain in Concept.sym.flags
sfExplain in typeClass.sym.flags
if collectDiagnostics:
oldWriteHook = writelnHook
@@ -684,7 +684,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
diagnostics = @[]
flags = {efExplain}
writelnHook = proc (s: string) =
if errorPrefix == nil: errorPrefix = Concept.sym.name.s & ":"
if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg
@@ -704,7 +704,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
put(m, p[1], p[0].typ)
if ff.kind == tyUserTypeClassInst:
result = generateTypeInstance(c, m.bindings, Concept.sym.info, ff)
result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
else:
result = copyType(ff, ff.owner, true)

View File

@@ -197,9 +197,6 @@ 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.
To help you write more concise implicitly generic procs, the Nim's system
module includes the named types `T1` through `T9` which are bind once aliases
of the `auto` type.
Concepts
--------
@@ -436,17 +433,35 @@ in the place of each missing generic param.
Please note that generic concepts such as `Enumerable[T]` can be matched
against concrete types such as `string`. Nim doesn't require the concept
type to have the same number of parameters as the type being matched.
In order to express such a requirement, you'll need to rely on a type
mapping operator such a `genericHead` or `stripGenericParams` within the
concept body:
If you wish to express a requirement towards the generic parameters of
the matched type, you can use a type mapping operator such as `genericHead`
or `stripGenericParams` within the body of the concept to obtain the
uninstantiated version of the type, which you can then try to instantiate
in any required way. For example, here is how one might define the classic
`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
type is an instance of it:
.. code-block:: nim
import future, typetraits
type
Functor[A] = concept f
f.value is A
map(f, A -> T1) is genericHead(f.type)[T1]
type MatchedGenericType = genericHead(f.type)
# `f` will be a value of a type such as `Option[T]`
# `MatchedGenericType` will become the `Option` type
f.val is A
# The Functor should provide a way to obtain
# a value stored inside it
type T = auto
map(f, A -> T) is MatchedGenericType[T]
# And it should provide a way to map one instance of
# the Functor to a instance of a different type, given
# a suitable `map` operation for the enclosed values
import options
echo Option[int] is Functor # prints true
Concept derived values

View File

@@ -98,17 +98,6 @@ type
SomeNumber* = SomeInteger|SomeReal
## type class matching all number types
T1* = auto
T2* = auto
T3* = auto
T4* = auto
T5* = auto
T6* = auto
T7* = auto
T8* = auto
T9* = auto
## Helper types for writing implicitly generic procs
proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
## Special compile-time procedure that checks whether `x` is
## defined.