concepts.nim 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168
  10. ## for details. Note this is a first implementation and only the "Concept matching"
  11. ## section has been implemented.
  12. import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
  13. import std/intsets
  14. when defined(nimPreviewSlimSystem):
  15. import std/assertions
  16. const
  17. logBindings = when defined(debugConcepts): true else: false
  18. ## Code dealing with Concept declarations
  19. ## --------------------------------------
  20. proc declareSelf(c: PContext; info: TLineInfo) =
  21. ## Adds the magical 'Self' symbols to the current scope.
  22. let ow = getCurrOwner(c)
  23. let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
  24. s.typ = newType(tyTypeDesc, c.idgen, ow)
  25. s.typ.flags.incl {tfUnresolved, tfPacked}
  26. s.typ.add newType(tyEmpty, c.idgen, ow)
  27. addDecl(c, s, info)
  28. proc semConceptDecl(c: PContext; n: PNode): PNode =
  29. ## Recursive helper for semantic checking for the concept declaration.
  30. ## Currently we only support (possibly empty) lists of statements
  31. ## containing 'proc' declarations and the like.
  32. case n.kind
  33. of nkStmtList, nkStmtListExpr:
  34. result = shallowCopy(n)
  35. for i in 0..<n.len:
  36. result[i] = semConceptDecl(c, n[i])
  37. of nkProcDef..nkIteratorDef, nkFuncDef:
  38. result = c.semExpr(c, n, {efWantStmt})
  39. of nkTypeClassTy:
  40. result = shallowCopy(n)
  41. for i in 0..<n.len-1:
  42. result[i] = n[i]
  43. result[^1] = semConceptDecl(c, n[^1])
  44. of nkCommentStmt:
  45. result = n
  46. else:
  47. localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
  48. result = n
  49. proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
  50. ## Semantic checking for the concept declaration. Runs
  51. ## when we process the concept itself, not its matching process.
  52. assert n.kind == nkTypeClassTy
  53. inc c.inConceptDecl
  54. openScope(c)
  55. declareSelf(c, n.info)
  56. result = semConceptDecl(c, n)
  57. rawCloseScope(c)
  58. dec c.inConceptDecl
  59. ## Concept matching
  60. ## ----------------
  61. type
  62. MatchFlags* = enum
  63. mfDontBind # Do not bind generic parameters
  64. mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
  65. MatchCon = object ## Context we pass around during concept matching.
  66. bindings: LayeredIdTable
  67. marker: IntSet ## Some protection against wild runaway recursions.
  68. potentialImplementation: PType ## the concrete type that might match the concept we try to match.
  69. magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
  70. ## cannot be fixed that easily.
  71. ## Thus we special case it here.
  72. concpt: PType ## current concept being evaluated
  73. depthCount = 0
  74. flags: set[MatchFlags]
  75. MatchKind = enum
  76. mkNoMatch, mkSubset, mkSame
  77. const
  78. asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
  79. bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
  80. proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
  81. proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
  82. proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
  83. proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
  84. proc existingBinding(m: MatchCon; key: PType): PType =
  85. ## checks if we bound the type variable 'key' already to some
  86. ## concrete type.
  87. result = m.bindings.lookup(key)
  88. if result == nil:
  89. result = key
  90. const
  91. ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
  92. proc unrollGenericParam(param: PType): PType =
  93. result = param.skipTypes(ignorableForArgType)
  94. while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
  95. result = result.elementType
  96. proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
  97. if v.kind == tyTypeDesc:
  98. return false
  99. var value = unrollGenericParam(v)
  100. if value.kind == tyGenericParam:
  101. value = existingBinding(m, value)
  102. if value.kind == tyGenericParam:
  103. if value.hasElementType:
  104. value = value.elementType
  105. else:
  106. return true
  107. if value.kind == tyStatic:
  108. return false
  109. if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
  110. value = value.last
  111. let old = existingBinding(m, key)
  112. if old != key:
  113. # check previously bound value
  114. if not matchType(c, old, value, m):
  115. return false
  116. elif key.hasElementType and key.elementType.kind != tyNone:
  117. # check constaint
  118. if matchType(c, unrollGenericParam(key), value, m) == false:
  119. return false
  120. when logBindings: echo "bind table adding '", key, "', ", value
  121. assert value != nil
  122. assert value.kind != tyVoid
  123. m.bindings.put(key, value)
  124. return true
  125. proc defSignatureType(n: PNode): PType = n[0].sym.typ
  126. proc conceptBody*(n: PType): PNode = n.n.lastSon
  127. proc acceptsAllTypes(t: PType): bool=
  128. result = false
  129. if t.kind == tyAnything:
  130. result = true
  131. elif t.kind == tyGenericParam:
  132. if tfImplicitTypeParam in t.flags:
  133. result = true
  134. if not t.hasElementType or t.elementType.kind == tyNone:
  135. result = true
  136. proc procDefSignature(s: PSym): PNode {. deprecated .} =
  137. var nc = s.ast.copyNode()
  138. for i in 0 .. 5:
  139. nc.add s.ast[i]
  140. nc
  141. proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
  142. result = true
  143. for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
  144. if not matchType(c, f[i], a[i], m): return false
  145. iterator traverseTyOr(t: PType): PType {. closure .}=
  146. for i in t.kids:
  147. case i.kind:
  148. of tyGenericParam:
  149. if i.hasElementType:
  150. for s in traverseTyOr(i.elementType):
  151. yield s
  152. else:
  153. yield i
  154. else:
  155. yield i
  156. proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
  157. assert not(potentialImpl.reduceToBase.kind == tyConcept)
  158. let concpt = f.reduceToBase
  159. if m.depthCount > 0:
  160. # concepts that are more then 2 levels deep are treated like
  161. # tyAnything to stop dependencies from getting out of control
  162. return true
  163. var efPot = potentialImpl
  164. if potentialImpl.isSelf:
  165. if m.concpt.n == concpt.n:
  166. return true
  167. efPot = m.potentialImplementation
  168. var oldBindings = m.bindings
  169. m.bindings = newTypeMapLayer(m.bindings)
  170. let oldPotentialImplementation = m.potentialImplementation
  171. m.potentialImplementation = efPot
  172. let oldConcept = m.concpt
  173. m.concpt = concpt
  174. var invocation: PType = nil
  175. if f.kind in {tyGenericInvocation, tyGenericInst}:
  176. invocation = f
  177. inc m.depthCount
  178. result = processConcept(c, concpt, invocation, oldBindings, m)
  179. dec m.depthCount
  180. m.potentialImplementation = oldPotentialImplementation
  181. m.concpt = oldConcept
  182. m.bindings = oldBindings
  183. proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
  184. if fn.kind != an.kind:
  185. return false
  186. if fn[namePos].sym.name != an[namePos].sym.name:
  187. return false
  188. let
  189. ft = fn.defSignatureType
  190. at = an.defSignatureType
  191. if ft.len != at.len:
  192. return false
  193. for i in 1 ..< ft.n.len:
  194. m.bindings = m.bindings.newTypeMapLayer()
  195. let aType = at.n[i].typ
  196. let fType = ft.n[i].typ
  197. if aType.isSelf and fType.isSelf:
  198. continue
  199. if not matchType(c, fType, aType, m):
  200. m.bindings.setToPreviousLayer()
  201. return false
  202. result = true
  203. if not matchReturnType(c, ft.returnType, at.returnType, m):
  204. m.bindings.setToPreviousLayer()
  205. result = false
  206. proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
  207. # XXX: In the future this may need extra parameters to carry info for container types
  208. if fc.n == ac.n:
  209. # This will have to take generic parameters into account at some point
  210. return mkSame
  211. let
  212. fn = fc.conceptBody
  213. an = ac.conceptBody
  214. sameLen = fc.len == ac.len
  215. var match = false
  216. for fdef in fn:
  217. var cmpResult = false
  218. for ia, ndef in an:
  219. match = cmpConceptDefs(c, fdef, ndef, m)
  220. if match:
  221. break
  222. if not match:
  223. return mkNoMatch
  224. return mkSubset
  225. proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
  226. ## The heart of the concept matching process. 'f' is the formal parameter of some
  227. ## routine inside the concept that we're looking for. 'a' is the formal parameter
  228. ## of a routine that might match.
  229. var
  230. a = ao
  231. f = fo
  232. if a.kind in bindableTypes:
  233. a = existingBinding(m, ao)
  234. if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
  235. a = a.elementType
  236. if f.isConcept:
  237. if a.acceptsAllTypes:
  238. return false
  239. if a.skipTypes(ignorableForArgType).isConcept:
  240. # if f is a subset of a then any match to a will also match f. Not the other way around
  241. return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
  242. else:
  243. return matchConceptToImpl(c, f, a, m)
  244. result = false
  245. case f.kind
  246. of tyAlias:
  247. result = matchType(c, f.skipModifier, a, m)
  248. of tyTypeDesc:
  249. if isSelf(f):
  250. let ua = a.skipTypes(asymmetricConceptParamMods)
  251. if m.magic in {mArrPut, mArrGet}:
  252. if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
  253. bindParam(c, m, a, last m.potentialImplementation)
  254. result = true
  255. #elif ua.isConcept:
  256. # result = matchType(c, m.concpt, ua, m)
  257. else:
  258. result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
  259. else:
  260. if a.kind == tyTypeDesc:
  261. if not(a.hasElementType) or a.elementType.kind == tyNone:
  262. result = true
  263. elif f.hasElementType:
  264. result = matchType(c, f.elementType, a.elementType, m)
  265. of tyVar, tySink, tyLent, tyOwned:
  266. # modifiers in the concept must be there in the actual implementation
  267. # too but not vice versa.
  268. if a.kind == f.kind:
  269. result = matchType(c, f.elementType, a.elementType, m)
  270. elif m.magic == mArrPut:
  271. result = matchType(c, f.elementType, a, m)
  272. of tyEnum, tyObject, tyDistinct:
  273. if a.kind in ignorableForArgType:
  274. result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
  275. else:
  276. if a.kind == tyGenericInst:
  277. # tyOr does this to generic typeclasses
  278. result = a.base.sym == f.sym
  279. else:
  280. result = sameType(f, a)
  281. of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
  282. result = a.skipTypes(ignorableForArgType).kind == f.kind
  283. of tyBool, tyChar, tyInt..tyUInt64:
  284. let ak = a.skipTypes(ignorableForArgType)
  285. result = ak.kind == f.kind or ak.kind == tyOrdinal or
  286. (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
  287. of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
  288. if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
  289. # XXX: this is a work-around!
  290. # system.nim creates these for the magic array typeclass
  291. result = true
  292. else:
  293. let ak = a.skipTypes(ignorableForArgType - {f.kind})
  294. if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
  295. result = matchKids(c, f, ak, m)
  296. of tyGenericInvocation, tyGenericInst:
  297. result = false
  298. let ea = a.skipTypes(ignorableForArgType)
  299. if ea.kind in {tyGenericInst, tyGenericInvocation}:
  300. var
  301. k1 = f.kidsLen - ord(f.kind == tyGenericInst)
  302. k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
  303. if sameType(f.genericHead, ea.genericHead) and k1 == k2:
  304. result = true
  305. for i in 1 ..< k2:
  306. if not matchType(c, f[i], ea[i], m):
  307. result = false
  308. break
  309. of tyOrdinal:
  310. result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
  311. of tyStatic:
  312. var scomp = f.base
  313. if scomp.kind == tyGenericParam:
  314. if f.base.kidsLen > 0:
  315. scomp = scomp.base
  316. if a.kind == tyStatic:
  317. result = matchType(c, scomp, a.base, m)
  318. else:
  319. result = matchType(c, scomp, a, m)
  320. of tyGenericParam:
  321. if a.acceptsAllTypes:
  322. discard bindParam(c, m, f, a)
  323. result = f.acceptsAllTypes
  324. else:
  325. result = bindParam(c, m, f, a)
  326. of tyAnything:
  327. result = true
  328. of tyNot:
  329. if a.kind == tyNot:
  330. result = matchType(c, f.elementType, a.elementType, m)
  331. else:
  332. m.bindings = m.bindings.newTypeMapLayer()
  333. result = not matchType(c, f.elementType, a, m)
  334. m.bindings.setToPreviousLayer()
  335. of tyAnd:
  336. m.bindings = m.bindings.newTypeMapLayer()
  337. result = true
  338. for ff in traverseTyOr(f):
  339. let r = matchType(c, ff, a, m)
  340. if not r:
  341. m.bindings.setToPreviousLayer()
  342. result = false
  343. break
  344. of tyGenericBody:
  345. var ak = a
  346. if a.kind == tyGenericBody:
  347. ak = last(a)
  348. result = matchType(c, last(f), ak, m)
  349. of tyCompositeTypeClass:
  350. if a.kind == tyCompositeTypeClass:
  351. result = matchKids(c, f, a, m)
  352. else:
  353. result = matchType(c, last(f), a, m)
  354. of tyBuiltInTypeClass:
  355. let target = f.genericHead.kind
  356. result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
  357. of tyOr:
  358. if a.kind == tyOr:
  359. var covered = 0
  360. for ff in traverseTyOr(f):
  361. for aa in traverseTyOr(a):
  362. m.bindings = m.bindings.newTypeMapLayer()
  363. let r = matchType(c, ff, aa, m)
  364. if r:
  365. inc covered
  366. break
  367. m.bindings.setToPreviousLayer()
  368. result = covered >= a.kidsLen
  369. else:
  370. for ff in f.kids:
  371. m.bindings = m.bindings.newTypeMapLayer()
  372. result = matchType(c, ff, a, m)
  373. if result: break # and remember the binding!
  374. m.bindings.setToPreviousLayer()
  375. else:
  376. result = false
  377. if result and ao.kind == tyGenericParam:
  378. let bf = if f.isSelf: m.potentialImplementation else: f
  379. if bindParam(c, m, ao, bf):
  380. when logBindings: echo " ^ reverse binding"
  381. proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
  382. result = matchType(c, f, a, m) or matchType(c, a, f, m)
  383. proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
  384. ## Like 'matchType' but with extra logic dealing with proc return types
  385. ## which can be nil or the 'void' type.
  386. if f.isEmptyType:
  387. result = a.isEmptyType
  388. elif a == nil:
  389. result = false
  390. else:
  391. result = checkConstraint(c, f, a, m)
  392. proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
  393. ## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
  394. ## or similar.
  395. # watch out: only add bindings after a completely successful match.
  396. m.bindings = m.bindings.newTypeMapLayer()
  397. let can = candidate.typ.n
  398. let con = defSignatureType(n).n
  399. if can.len < con.len:
  400. # too few arguments, cannot be a match:
  401. return false
  402. if can.len > con.len:
  403. # too many arguments (not optional)
  404. for i in con.len ..< can.len:
  405. if can[i].sym.ast == nil:
  406. return false
  407. when defined(debugConcepts):
  408. echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
  409. let common = min(can.len, con.len)
  410. for i in 1 ..< common:
  411. if not checkConstraint(c, con[i].typ, can[i].typ, m):
  412. m.bindings.setToPreviousLayer()
  413. return false
  414. if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
  415. m.bindings.setToPreviousLayer()
  416. return false
  417. # all other parameters have to be optional parameters:
  418. for i in common ..< can.len:
  419. assert can[i].kind == nkSym
  420. if can[i].sym.ast == nil:
  421. # has too many arguments one of which is not optional:
  422. m.bindings.setToPreviousLayer()
  423. return false
  424. return true
  425. proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
  426. ## Walk the current scope, extract candidates which the same name as 'n[namePos]',
  427. ## 'n' is the nkProcDef or similar from the concept that we try to match.
  428. result = false
  429. var candidates = searchScopes(c, n[namePos].sym.name, kinds)
  430. searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
  431. for candidate in candidates:
  432. m.magic = candidate.magic
  433. if matchSym(c, candidate, n, m):
  434. result = true
  435. break
  436. proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
  437. ## Traverse the concept's AST ('n') and see if every declaration inside 'n'
  438. ## can be matched with the current scope.
  439. case n.kind
  440. of nkStmtList, nkStmtListExpr:
  441. for i in 0..<n.len:
  442. if not conceptMatchNode(c, n[i], m):
  443. return false
  444. return true
  445. of nkProcDef, nkFuncDef:
  446. # procs match any of: proc, template, macro, func, method, converter.
  447. # The others are more specific.
  448. # XXX: Enforce .noSideEffect for 'nkFuncDef'? But then what are the use cases...
  449. const filter = {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}
  450. result = matchSyms(c, n, filter, m)
  451. of nkTemplateDef:
  452. result = matchSyms(c, n, {skTemplate}, m)
  453. of nkMacroDef:
  454. result = matchSyms(c, n, {skMacro}, m)
  455. of nkConverterDef:
  456. result = matchSyms(c, n, {skConverter}, m)
  457. of nkMethodDef:
  458. result = matchSyms(c, n, {skMethod}, m)
  459. of nkIteratorDef:
  460. result = matchSyms(c, n, {skIterator}, m)
  461. of nkCommentStmt:
  462. result = true
  463. else:
  464. # error was reported earlier.
  465. result = false
  466. proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; m: var MatchCon) =
  467. # invocation != nil means we have a non-atomic concept:
  468. if invocation != nil and invocation.kind == tyGenericInvocation:
  469. assert concpt.sym.typ.kind == tyGenericBody
  470. for i in 0 .. concpt.sym.typ.len - 1:
  471. let thisSym = concpt.sym.typ[i]
  472. if lookup(bindings, thisSym) != nil:
  473. # dont trust the bindings over existing ones
  474. continue
  475. let found = m.bindings.lookup(thisSym)
  476. if found != nil:
  477. when logBindings: echo "Invocation bind: ", thisSym, " ", found
  478. bindings.put(thisSym, found)
  479. # bind even more generic parameters
  480. let genBody = invocation.base
  481. assert genBody.kind == tyGenericBody
  482. for i in FirstGenericParamAt ..< invocation.kidsLen:
  483. let bpram = genBody[i - 1]
  484. if lookup(bindings, invocation[i]) != nil:
  485. # dont trust the bindings over existing ones
  486. continue
  487. let boundV = lookup(bindings, bpram)
  488. when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
  489. if boundV != nil:
  490. bindings.put(invocation[i], boundV)
  491. bindings.put(concpt, m.potentialImplementation)
  492. proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
  493. m.bindings = m.bindings.newTypeMapLayer()
  494. if invocation != nil and invocation.kind == tyGenericInst:
  495. let genericBody = invocation.base
  496. for i in 1..<invocation.kidsLen-1:
  497. # instGenericContainer can bind `tyVoid`
  498. if invocation[i].kind != tyVoid:
  499. bindParam(c, m, genericBody[i-1], invocation[i])
  500. result = conceptMatchNode(c, concpt.conceptBody, m)
  501. if result and mfDontBind notin m.flags:
  502. fixBindings(bindings, concpt, invocation, m)
  503. proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
  504. ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
  505. ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
  506. ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
  507. ## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
  508. ## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
  509. ## `C[S, T]` parent type that we look for. We need this because we need to store bindings
  510. ## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
  511. ## we do not add any bindings at all on an unsuccessful match!
  512. var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
  513. if arg.isConcept:
  514. result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
  515. elif arg.acceptsAllTypes:
  516. # XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
  517. result = false
  518. elif mfCheckGeneric in m.flags:
  519. # prioritize concepts the least. Specifically if the arg is not a catch all as per above
  520. result = true
  521. else:
  522. result = processConcept(c, concpt, invocation, bindings, m)