Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
008ffa4
Infer generic bindings
Jul 22, 2023
bc08ff2
Simple test
Jul 22, 2023
257fd33
Add t
Jul 22, 2023
0c6d8bb
Allow it to work for templates too
Jul 22, 2023
d5cbc7e
Fix some builds by putting bindings in a template
Jul 22, 2023
f2ed402
Fix builtins
Jul 23, 2023
cf36fa8
Slightly more exotic seq test
Jul 23, 2023
a235f14
Test value-based generics using array
Jul 23, 2023
e331214
Pass expectedType into buildBindings
Jul 23, 2023
851fbcf
Put buildBindings into a proc
Jul 23, 2023
04f7e08
Manual entry
Jul 23, 2023
d607d26
Remove leftover `
Jul 23, 2023
f915036
Improve language used in the manual
Jul 24, 2023
165673b
Experimental flag and fix basic constructors
Jul 24, 2023
9e42bbc
Tiny commend cleanup
Jul 24, 2023
e66af09
Move to experimental manual
Jul 24, 2023
444c169
Use 'kind' so tuples continue to fail like before
Jul 24, 2023
b554162
Explicitly disallow tuples
Jul 24, 2023
1cad79f
Table test and document tuples
Jul 24, 2023
189c9e1
Test type reduction
Jul 24, 2023
6d85aeb
Disable inferGenericTypes check for CI tests
Jul 24, 2023
b006c4e
Remove tuple info in manual
Jul 24, 2023
347eb20
Always reduce types. Testing CI
Jul 26, 2023
2a8353c
Fixes
Jul 26, 2023
dccee38
Ignore tyGenericInst
Jul 26, 2023
be98387
Prevent binding already bound generic params
Jul 29, 2023
fe2b42e
tyUncheckedArray
Jul 29, 2023
524a289
Few more types
Jul 29, 2023
d926772
Update manual and check for flag again
Jul 29, 2023
fbde75b
Update tests/generics/treturn_inference.nim
Araq Aug 2, 2023
970fafc
var candidate, remove flag check again for CI
Aug 2, 2023
18465fa
Enable check once more
Aug 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,8 @@ proc getCallLineInfo(n: PNode): TLineInfo =
result = n.info

proc semResolvedCall(c: PContext, x: TCandidate,
n: PNode, flags: TExprFlags): PNode =
n: PNode, flags: TExprFlags;
expectedType: PType = nil): PNode =
assert x.state == csMatch
var finalCallee = x.calleeSym
let info = getCallLineInfo(n)
Expand All @@ -577,17 +578,32 @@ proc semResolvedCall(c: PContext, x: TCandidate,
result.typ = newTypeS(x.fauxMatch, c)
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
return

template buildBindings(x: TCandidate, expectedType: PType): TIdTable =
## helper template to pass along bound generic parameters from expectedType
var bindings = x.bindings
if expectedType != nil and expectedType.sons.len() > 0 and expectedType.sons[0] != nil:
let y = x.calleeSym.ast[genericParamsPos]
# concrete types give us just the list of generic params
let startIdx = if expectedType.kind in ConcreteTypes: 0 else: 1
for i in startIdx ..< expectedType.len-startIdx:
let j = i - startIdx # idx of unbound param in callee
if bindings.idTableGet(y[j].typ) != nil:
break # let's not overwrite existing ones
bindings.idTablePut(y[j].typ, expectedType[i])
bindings

let gp = finalCallee.ast[genericParamsPos]
if gp.isGenericParams:
if x.calleeSym.kind notin {skMacro, skTemplate}:
if x.calleeSym.magic in {mArrGet, mArrPut}:
finalCallee = x.calleeSym
else:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
finalCallee = generateInstance(c, x.calleeSym, x.buildBindings(expectedType), n.info)
else:
# For macros and templates, the resolved generic params
# are added as normal params.
for s in instantiateGenericParamList(c, gp, x.bindings):
for s in instantiateGenericParamList(c, gp, x.buildBindings(expectedType)):
case s.kind
of skConst:
if not s.astdef.isNil:
Expand Down Expand Up @@ -615,7 +631,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:
Expand All @@ -625,7 +642,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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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));
Expand Down
78 changes: 78 additions & 0 deletions tests/generics/treturn_inference.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

block:
type
MyOption[T, Z] = object
x: T
y: Z

proc none[T, Z](): MyOption[T, Z] =
when T is int:
result.x = 22
when Z is float:
result.y = 12.0

proc myGenericProc[T, Z](): MyOption[T, Z] =
none() # implied by return type

let a = myGenericProc[int, float]()
doAssert a.x == 22
doAssert a.y == 12.0

let b: MyOption[int, float] = none() # implied by type of b
doAssert b.x == 22
doAssert b.y == 12.0

# Simple template based result with inferred type for errors
block:
type
ResultKind {.pure.} = enum
Ok
Err

Result[T] = object
case kind: ResultKind
of Ok:
data: T
of Err:
errmsg: cstring

template err[T](msg: static cstring): Result[T] =
Result[T](kind : ResultKind.Err, errmsg : msg)

proc testproc(): Result[int] =
err("Inferred error!") # implied by proc return
let r = testproc()
doAssert r.kind == ResultKind.Err
doAssert r.errmsg == "Inferred error!"

# Builtin seq
block:
let x: seq[int] = newSeq(1)
doAssert x is seq[int]
doAssert x.len() == 1

type
MyType[T, Z] = object
x: T
y: Z

let y: seq[MyType[int, float]] = newSeq(2)
doAssert y is seq[MyType[int, float]]
doAssert y.len() == 2

let z = MyType[seq[float], string](
x : newSeq(3),
y : "test"
)
doAssert z.x is seq[float]
doAssert z.x.len() == 3
doAssert z.y is string
doAssert z.y == "test"

# array
block:
proc giveArray[N, T](): array[N, T] =
for i in 0 .. N.high:
result[i] = i
var x: array[2, int] = giveArray()
doAssert x == [0, 1]