diff options
-rw-r--r-- | compiler/semcall.nim | 30 | ||||
-rw-r--r-- | compiler/semstmts.nim | 17 | ||||
-rw-r--r-- | compiler/semtypes.nim | 2 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 24 | ||||
-rw-r--r-- | doc/manual/generics.txt | 31 | ||||
-rw-r--r-- | lib/system.nim | 11 |
6 files changed, 66 insertions, 49 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim index e1f09c3d5..f707fc844 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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}) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 12391a9cd..a678311bf 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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]) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 02166f0aa..422d2f0fa 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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 = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index bbcf25903..ff7b0ae72 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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) diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index bb5d0ab2c..87fcb7828 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -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 diff --git a/lib/system.nim b/lib/system.nim index 0e777b707..94e10d7df 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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. |