requested code review changes

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

View file

@ -1555,9 +1555,14 @@ proc usesResult(n: PNode): bool =
for c in n: for c in n:
if usesResult(c): return true 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) 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 typ.n = res
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = 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: if c.inTypeClass > 0 and expr.typ != nil:
case expr.typ.kind case expr.typ.kind
of tyBool: 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: if expr[1].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[1].typ, expr[2]) inferConceptStaticParam(c, expr[1], expr[2])
continue continue
elif expr[2].typ.isUnresolvedStatic: elif expr[2].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[2].typ, expr[1]) inferConceptStaticParam(c, expr[2], expr[1])
continue continue
let verdict = semConstExpr(c, n[i]) let verdict = semConstExpr(c, n[i])

View file

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

View file

@ -22,10 +22,10 @@ type
TCandidateState* = enum TCandidateState* = enum
csEmpty, csMatch, csNoMatch csEmpty, csMatch, csNoMatch
CandidateError = tuple CandidateError* = object
sym: PSym sym*: PSym
unmatchedVarParam: int unmatchedVarParam*: int
diagnostics: seq[string] diagnostics*: seq[string]
CandidateErrors* = seq[CandidateError] CandidateErrors* = seq[CandidateError]
@ -589,8 +589,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): PType = ff, a: PType): PType =
var var
Concept = ff.skipTypes({tyUserTypeClassInst}) typeClass = ff.skipTypes({tyUserTypeClassInst})
body = Concept.n[3] body = typeClass.n[3]
if c.inTypeClass > 4: if c.inTypeClass > 4:
localError(body.info, $body & " too nested for type matching") localError(body.info, $body & " too nested for type matching")
return nil return nil
@ -611,7 +611,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
param: PSym param: PSym
template paramSym(kind): untyped = template paramSym(kind): untyped =
newSym(kind, typeParamName, Concept.sym, Concept.sym.info) newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
block addTypeParam: block addTypeParam:
for prev in typeParams: for prev in typeParams:
@ -642,7 +642,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
addDecl(c, param) addDecl(c, param)
for param in Concept.n[0]: for param in typeClass.n[0]:
var var
dummyName: PNode dummyName: PNode
dummyType: PType dummyType: PType
@ -665,7 +665,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
internalAssert dummyName.kind == nkIdent internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, 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 dummyParam.typ = dummyType
addDecl(c, dummyParam) addDecl(c, dummyParam)
@ -675,7 +675,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
errorPrefix: string errorPrefix: string
flags: TExprFlags = {} flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or collectDiagnostics = m.diagnostics != nil or
sfExplain in Concept.sym.flags sfExplain in typeClass.sym.flags
if collectDiagnostics: if collectDiagnostics:
oldWriteHook = writelnHook oldWriteHook = writelnHook
@ -684,7 +684,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
diagnostics = @[] diagnostics = @[]
flags = {efExplain} flags = {efExplain}
writelnHook = proc (s: string) = 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) let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg diagnostics.add msg
@ -704,7 +704,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
put(m, p[1], p[0].typ) put(m, p[1], p[0].typ)
if ff.kind == tyUserTypeClassInst: if ff.kind == tyUserTypeClassInst:
result = generateTypeInstance(c, m.bindings, Concept.sym.info, ff) result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
else: else:
result = copyType(ff, ff.owner, true) 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 be inferred to have the equivalent of the `any` type class and thus they will
match anything without discrimination. 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 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 Please note that generic concepts such as `Enumerable[T]` can be matched
against concrete types such as `string`. Nim doesn't require the concept 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. 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 If you wish to express a requirement towards the generic parameters of
mapping operator such a `genericHead` or `stripGenericParams` within the the matched type, you can use a type mapping operator such as `genericHead`
concept body: 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 .. code-block:: nim
import future, typetraits import future, typetraits
type type
Functor[A] = concept f Functor[A] = concept f
f.value is A type MatchedGenericType = genericHead(f.type)
map(f, A -> T1) is genericHead(f.type)[T1] # `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 Concept derived values

View file

@ -98,17 +98,6 @@ type
SomeNumber* = SomeInteger|SomeReal SomeNumber* = SomeInteger|SomeReal
## type class matching all number types ## 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.} proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
## Special compile-time procedure that checks whether `x` is ## Special compile-time procedure that checks whether `x` is
## defined. ## defined.