mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-29 19:57:58 +00:00
Add experimental inferGenericTypes switch (#22317)
* Infer generic bindings * Simple test * Add t * Allow it to work for templates too * Fix some builds by putting bindings in a template * Fix builtins * Slightly more exotic seq test * Test value-based generics using array * Pass expectedType into buildBindings * Put buildBindings into a proc * Manual entry * Remove leftover ` * Improve language used in the manual * Experimental flag and fix basic constructors * Tiny commend cleanup * Move to experimental manual * Use 'kind' so tuples continue to fail like before * Explicitly disallow tuples * Table test and document tuples * Test type reduction * Disable inferGenericTypes check for CI tests * Remove tuple info in manual * Always reduce types. Testing CI * Fixes * Ignore tyGenericInst * Prevent binding already bound generic params * tyUncheckedArray * Few more types * Update manual and check for flag again * Update tests/generics/treturn_inference.nim * var candidate, remove flag check again for CI * Enable check once more --------- Co-authored-by: SirOlaf <> Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
@@ -220,7 +220,8 @@ type
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictCaseObjects
|
||||
strictCaseObjects,
|
||||
inferGenericTypes
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
|
||||
@@ -562,8 +562,61 @@ proc getCallLineInfo(n: PNode): TLineInfo =
|
||||
discard
|
||||
result = n.info
|
||||
|
||||
proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
n: PNode, flags: TExprFlags): PNode =
|
||||
proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
## Helper proc to inherit bound generic parameters from expectedType into x.
|
||||
## Does nothing if 'inferGenericTypes' isn't in c.features
|
||||
if inferGenericTypes notin c.features: return
|
||||
if expectedType == nil or x.callee[0] == nil: return # required for inference
|
||||
|
||||
var
|
||||
flatUnbound: seq[PType]
|
||||
flatBound: seq[PType]
|
||||
# seq[(result type, expected type)]
|
||||
var typeStack = newSeq[(PType, PType)]()
|
||||
|
||||
template stackPut(a, b) =
|
||||
## skips types and puts the skipped version on stack
|
||||
# It might make sense to skip here one by one. It's not part of the main
|
||||
# type reduction because the right side normally won't be skipped
|
||||
const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass }
|
||||
let
|
||||
x = a.skipTypes(toSkip)
|
||||
y = if a.kind notin toSkip: b
|
||||
else: b.skipTypes(toSkip)
|
||||
typeStack.add((x, y))
|
||||
|
||||
stackPut(x.callee[0], expectedType)
|
||||
|
||||
while typeStack.len() > 0:
|
||||
let (t, u) = typeStack.pop()
|
||||
if t == u or t == nil or u == nil or t.kind == tyAnything or u.kind == tyAnything:
|
||||
continue
|
||||
case t.kind
|
||||
of ConcreteTypes, tyGenericInvocation, tyUncheckedArray:
|
||||
# nested, add all the types to stack
|
||||
let
|
||||
startIdx = if u.kind in ConcreteTypes: 0 else: 1
|
||||
endIdx = min(u.sons.len() - startIdx, t.sons.len())
|
||||
|
||||
for i in startIdx ..< endIdx:
|
||||
# early exit with current impl
|
||||
if t[i] == nil or u[i] == nil: return
|
||||
stackPut(t[i], u[i])
|
||||
of tyGenericParam:
|
||||
if x.bindings.idTableGet(t) != nil: return
|
||||
|
||||
# fully reduced generic param, bind it
|
||||
if t notin flatUnbound:
|
||||
flatUnbound.add(t)
|
||||
flatBound.add(u)
|
||||
else:
|
||||
discard
|
||||
for i in 0 ..< flatUnbound.len():
|
||||
x.bindings.idTablePut(flatUnbound[i], flatBound[i])
|
||||
|
||||
proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
n: PNode, flags: TExprFlags;
|
||||
expectedType: PType = nil): PNode =
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
let info = getCallLineInfo(n)
|
||||
@@ -583,10 +636,12 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
if x.calleeSym.magic in {mArrGet, mArrPut}:
|
||||
finalCallee = x.calleeSym
|
||||
else:
|
||||
c.inheritBindings(x, expectedType)
|
||||
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
|
||||
else:
|
||||
# For macros and templates, the resolved generic params
|
||||
# are added as normal params.
|
||||
c.inheritBindings(x, expectedType)
|
||||
for s in instantiateGenericParamList(c, gp, x.bindings):
|
||||
case s.kind
|
||||
of skConst:
|
||||
@@ -615,7 +670,8 @@ proc tryDeref(n: PNode): PNode =
|
||||
result.add n
|
||||
|
||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds, flags: TExprFlags): PNode =
|
||||
filter: TSymKinds, flags: TExprFlags;
|
||||
expectedType: PType = nil): PNode =
|
||||
var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
|
||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
||||
if r.state == csMatch:
|
||||
@@ -625,7 +681,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
message(c.config, n.info, hintUserRaw,
|
||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||
candidates)
|
||||
result = semResolvedCall(c, r, n, flags)
|
||||
result = semResolvedCall(c, r, n, flags, expectedType)
|
||||
else:
|
||||
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
result = semGenericStmt(c, n)
|
||||
|
||||
@@ -135,7 +135,7 @@ type
|
||||
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
||||
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.}
|
||||
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
|
||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
||||
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
|
||||
|
||||
@@ -952,17 +952,17 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
result = fixupTypeAfterEval(c, result, a)
|
||||
|
||||
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
flags: TExprFlags): PNode =
|
||||
flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
|
||||
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
||||
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
|
||||
# for typeof support.
|
||||
# for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType)
|
||||
else:
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType)
|
||||
|
||||
if result != nil:
|
||||
if result[0].kind != nkSym:
|
||||
@@ -1138,7 +1138,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType =
|
||||
# this seems to be a hotspot in the compiler!
|
||||
let nOrig = n.copyTree
|
||||
#semLazyOpAux(c, n)
|
||||
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
|
||||
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags, expectedType)
|
||||
if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
|
||||
else: result = errorNode(c, n)
|
||||
|
||||
@@ -3120,7 +3120,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
|
||||
else: result = semMagic(c, n, s, flags, expectedType)
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||
if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
|
||||
else: result = semMagic(c, n, s, flags, expectedType)
|
||||
else:
|
||||
#liMessage(n.info, warnUser, renderTree(n));
|
||||
|
||||
Reference in New Issue
Block a user