requested code review changes
This commit is contained in:
parent
01207b6cfd
commit
a74ad869e9
6 changed files with 66 additions and 49 deletions
|
|
@ -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})
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue