top-down type inference, implements rfc 149 (#20091)

* micro implementation of rfc 149

refs https://github.com/nim-lang/RFCs/issues/149

* number/array/seq literals, more statements

* try fix number literal alias issue

* renew expectedType with if/case/try branch types

* fix (nerf) index type handling and float typed int

* use typeAllowed

* tweaks + const test (tested locally) [skip ci]

* fill out more of the checklist

* more literals, change @ order, type conversions

Not copying the full call tree before the typedesc call check
in `semIndirectOp` is also a small performance improvement.

* disable self-conversion warning

* revert type conversions (maybe separate op later)

* deal with CI for now (seems unrelated), try enums

* workaround CI different way

* proper fix

* again

* see sizes

* lol

* overload selection, simplify int literal -> float

* range, new @ solution, try use fitNode for nil

* use new magic

* try fix ranges, new magic, deal with #20193

* add documentation, support templates

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 0014b9c48e)
This commit is contained in:
metagn
2022-08-24 08:11:41 +03:00
committed by narimiran
parent 01ce56ab9e
commit 037f87d904
17 changed files with 601 additions and 200 deletions

View File

@@ -670,7 +670,7 @@ type
mInSet, mRepr, mExit,
mSetLengthStr, mSetLengthSeq,
mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq,
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
mNewString, mNewStringOfCap, mParseBiggestFloat,
mMove, mWasMoved, mDestroy, mTrace,
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
@@ -704,8 +704,8 @@ type
mSymIsInstantiationOf, mNodeId, mPrivateAccess
# things that we can evaluate safely at compile time, even if not asked for it:
const
# things that we can evaluate safely at compile time, even if not asked for it:
ctfeWhitelist* = {mNone, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq,
@@ -734,6 +734,9 @@ const
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInSet, mRepr}
generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
## magics that are generated as normal procs in the backend
type
ItemId* = object
@@ -1679,6 +1682,10 @@ proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
transitionNodeKindCommon(kind)
n.intVal = obj.intVal
proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) =
transitionNodeKindCommon(kind)
n.floatVal = BiggestFloat(obj.intVal)
proc transitionNoneToSym*(n: PNode) =
transitionNodeKindCommon(nkSym)

View File

@@ -2384,7 +2384,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
genDollar(p, e, d, "#nimFloatToStr($1)")
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr, mUnown: expr(p, e[1], d)
of mIsolate, mFinished: genCall(p, e, d)
of generatedMagics: genCall(p, e, d)
of mEnumToStr:
if optTinyRtti in p.config.globalOptions:
genEnumToStr(p, e, d)
@@ -2979,7 +2979,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if n[genericParamsPos].kind == nkEmpty:
var prc = n[namePos].sym
if useAliveDataFromDce in p.module.flags:
if p.module.alive.contains(prc.itemId.item) and prc.magic in {mNone, mIsolate, mFinished}:
if p.module.alive.contains(prc.itemId.item) and
prc.magic in generatedMagics:
genProc(p.module, prc)
elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or

View File

@@ -141,3 +141,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasEnforceNoRaises")
defineSymbol("nimHasWarnBareExcept")
defineSymbol("nimHasTopDownInference")

View File

@@ -1451,7 +1451,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
s.name.s)
discard mangleName(p.module, s)
r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic notin {mNone, mIsolate} or
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
{sfImportc, sfInfixCall} * s.flags != {}:
discard
elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty:
@@ -2102,6 +2102,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n[1], x)
useMagic(p, "nimCopy")
r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
of mOpenArrayToSeq:
genCall(p, n, r)
of mDestroy, mTrace: discard "ignore calls to the default destructor"
of mOrd: genOrd(p, n, r)
of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
@@ -2618,7 +2620,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
let s = n[namePos].sym
discard mangleName(p.module, s)
r.res = s.loc.r
if lfNoDecl in s.loc.flags or s.magic notin {mNone, mIsolate}: discard
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
elif not p.g.generatedSyms.containsOrIncl(s.id):
p.locals.add(genProc(p, s))
of nkType: r.res = genTypeInfo(p, n.typ)

View File

@@ -27,11 +27,11 @@ when not defined(leanCompiler):
# implementation
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
proc semExprNoType(c: PContext, n: PNode): PNode
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semProcBody(c: PContext, n: PNode): PNode
proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
@@ -48,7 +48,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
proc finishMethod(c: PContext, s: PSym)
proc evalAtCompileTime(c: PContext, n: PNode): PNode
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc semStaticExpr(c: PContext, n: PNode): PNode
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
proc semTypeOf(c: PContext; n: PNode): PNode
proc computeRequiresInit(c: PContext, t: PType): bool
@@ -269,12 +269,12 @@ proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
typeAllowedCheck(c, typ.n.info, typ, skProc)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode
flags: TExprFlags = {}; expectedType: PType = nil): PNode
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}): PNode
flags: TExprFlags = {}; expectedType: PType = nil): PNode
proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
if t.sym != nil: return t.sym
@@ -334,8 +334,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
isArrayConstr(arg):
arg.typ = eOrig.typ
proc tryConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
var e = semExprWithType(c, n, expectedType = expectedType)
if e == nil: return
result = getConstExpr(c.module, e, c.idgen, c.graph)
@@ -365,8 +365,8 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
const
errConstExprExpected = "constant expression expected"
proc semConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
var e = semExprWithType(c, n, expectedType = expectedType)
if e == nil:
localError(c.config, n.info, errConstExprExpected)
return n
@@ -388,14 +388,14 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
else:
result = fixupTypeAfterEval(c, result, e)
proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
if efNeedStatic in flags:
if efPreferNilResult in flags:
return tryConstExpr(c, n)
return tryConstExpr(c, n, expectedType)
else:
return semConstExpr(c, n)
return semConstExpr(c, n, expectedType)
else:
result = semExprWithType(c, n, flags)
result = semExprWithType(c, n, flags, expectedType)
if efPreferStatic in flags:
var evaluated = getConstExpr(c.module, result, c.idgen, c.graph)
if evaluated != nil: return evaluated
@@ -414,7 +414,7 @@ proc resetSemFlag(n: PNode) =
resetSemFlag(n[i])
proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
s: PSym, flags: TExprFlags): PNode =
s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
## Semantically check the output of a macro.
## This involves processes such as re-checking the macro output for type
## coherence, making sure that variables declared with 'let' aren't
@@ -438,10 +438,10 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
case retType.kind
of tyUntyped, tyAnything:
# Not expecting a type here allows templates like in ``tmodulealias.in``.
result = semExpr(c, result, flags)
result = semExpr(c, result, flags, expectedType)
of tyTyped:
# More restrictive version.
result = semExprWithType(c, result, flags)
result = semExprWithType(c, result, flags, expectedType)
of tyTypeDesc:
if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType)
var typ = semTypeNode(c, result, nil)
@@ -465,7 +465,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
result = semExpr(c, result, flags)
result = semExpr(c, result, flags, expectedType)
result = fitNode(c, retType, result, result.info)
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
dec(c.config.evalTemplateCounter)
@@ -476,7 +476,7 @@ const
errFloatToString = "cannot convert '$1' to '$2'"
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}): PNode =
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
rememberExpansion(c, nOrig.info, sym)
pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
@@ -496,7 +496,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
# c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, sym, flags)
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
if c.config.macrosToExpand.hasKey(sym.name.s):
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
result = wrapInComesFrom(nOrig.info, sym, result)
@@ -507,7 +507,7 @@ proc forceBool(c: PContext, n: PNode): PNode =
if result == nil: result = n
proc semConstBoolExpr(c: PContext, n: PNode): PNode =
result = forceBool(c, semConstExpr(c, n))
result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
if result.kind != nkIntLit:
localError(c.config, n.info, errConstExprExpected)

View File

@@ -118,10 +118,10 @@ type
symMapping*: TIdTable # every gensym'ed symbol needs to be mapped
# to some new symbol in a generic instantiation
libs*: seq[PLib] # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.}
computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool

View File

@@ -26,7 +26,7 @@ const
errUndeclaredFieldX = "undeclared field: '$1'"
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode =
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
rememberExpansion(c, n.info, s)
let info = getCallLineInfo(n)
markUsed(c, info, s)
@@ -36,7 +36,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
pushInfoContext(c.config, n.info, s.detailedInfo)
result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache,
c.templInstCounter, c.idgen, efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, s, flags, expectedType)
popInfoContext(c.config)
# XXX: A more elaborate line info rewrite might be needed
@@ -65,9 +66,9 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode =
rejectEmptyNode(n)
result = semExpr(c, n, flags+{efWantValue})
result = semExpr(c, n, flags+{efWantValue}, expectedType)
let
isEmpty = result.kind == nkEmpty
@@ -82,8 +83,8 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode =
# do not produce another redundant error message:
result = errorNode(c, n)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExprCheck(c, n, flags)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
result = semExprCheck(c, n, flags, expectedType)
if result.typ == nil and efInTypeof in flags:
result.typ = c.voidType
elif result.typ == nil or result.typ == c.enforceVoidContext:
@@ -261,7 +262,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool =
let s = qualifiedLookUp(c, n, {})
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
proc semConv(c: PContext, n: PNode): PNode =
proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
if n.len != 2:
localError(c.config, n.info, "a type conversion takes exactly one argument")
return n
@@ -277,7 +278,7 @@ proc semConv(c: PContext, n: PNode): PNode =
else:
targetType = targetType.base
of tyStatic:
var evaluated = semStaticExpr(c, n[1])
var evaluated = semStaticExpr(c, n[1], expectedType)
if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
result = n
result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
@@ -589,21 +590,36 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
typ[0] = makeRangeType(c, 0, n.len - 1, n.info)
result = typ
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = newNodeI(nkBracket, n.info)
result.typ = newTypeS(tyArray, c)
var expectedElementType, expectedIndexType: PType = nil
if expectedType != nil:
let expected = expectedType.skipTypes(abstractRange-{tyDistinct})
case expected.kind
of tyArray:
expectedIndexType = expected[0]
expectedElementType = expected[1]
of tyOpenArray:
expectedElementType = expected[0]
else: discard
rawAddSon(result.typ, nil) # index type
var
firstIndex, lastIndex: Int128
indexType = getSysType(c.graph, n.info, tyInt)
lastValidIndex = lastOrd(c.config, indexType)
if n.len == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
rawAddSon(result.typ,
if expectedElementType != nil and
typeAllowed(expectedElementType, skLet, c) == nil:
expectedElementType
else:
newTypeS(tyEmpty, c)) # needs an empty basetype!
lastIndex = toInt128(-1)
else:
var x = n[0]
if x.kind == nkExprColonExpr and x.len == 2:
var idx = semConstExpr(c, x[0])
var idx = semConstExpr(c, x[0], expectedIndexType)
if not isOrdinalType(idx.typ):
localError(c.config, idx.info, "expected ordinal value for array " &
"index, got '$1'" % renderTree(idx))
@@ -614,8 +630,10 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
lastValidIndex = lastOrd(c.config, indexType)
x = x[1]
let yy = semExprWithType(c, x)
let yy = semExprWithType(c, x, expectedType = expectedElementType)
var typ = yy.typ
if expectedElementType == nil:
expectedElementType = typ
result.add yy
#var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
for i in 1..<n.len:
@@ -627,13 +645,13 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
x = n[i]
if x.kind == nkExprColonExpr and x.len == 2:
var idx = semConstExpr(c, x[0])
var idx = semConstExpr(c, x[0], indexType)
idx = fitNode(c, indexType, idx, x.info)
if lastIndex+1 != getOrdValue(idx):
localError(c.config, x.info, "invalid order in array constructor")
x = x[1]
let xx = semExprWithType(c, x, {})
let xx = semExprWithType(c, x, {}, expectedElementType)
result.add xx
typ = commonType(c, typ, xx.typ)
#n[i] = semExprWithType(c, x, {})
@@ -862,10 +880,10 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode): PNode =
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
inc c.inStaticContext
openScope(c)
let a = semExprWithType(c, n)
let a = semExprWithType(c, n, expectedType = expectedType)
closeScope(c)
dec c.inStaticContext
if a.findUnresolvedStatic != nil: return a
@@ -909,7 +927,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
rawAddSon(typ, result.typ)
result.typ = typ
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
@@ -932,7 +950,7 @@ proc setGenericParams(c: PContext, n: PNode) =
for i in 1..<n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
return errorNode(c, n)
@@ -947,8 +965,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
let callee = result[0].sym
case callee.kind
of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
of skTemplate: result = semTemplateExpr(c, result, callee, flags)
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
else:
semFinishOperands(c, result)
activate(c, result)
@@ -964,7 +982,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
if c.matchedConcept == nil:
result = evalAtCompileTime(c, result)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = nil
checkMinSonsLen(n, 1, c.config)
var prc = n[0]
@@ -989,16 +1007,20 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
let s = bracketedMacro(n[0])
if s != nil:
setGenericParams(c, n[0])
return semDirectOp(c, n, flags)
return semDirectOp(c, n, flags, expectedType)
elif isSymChoice(n[0]):
# overloaded generic procs e.g. newSeq[int] can end up here
return semDirectOp(c, n, flags)
return semDirectOp(c, n, flags, expectedType)
let nOrig = n.copyTree
semOpAux(c, n)
var t: PType = nil
if n[0].typ != nil:
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
if t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
return semConv(c, n)
let nOrig = n.copyTree
semOpAux(c, n)
if t != nil and t.kind == tyProc:
# This is a proc variable, apply normal overload resolution
let m = resolveIndirectCall(c, n, nOrig, t)
@@ -1037,9 +1059,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = m.call
instGenericConvertersSons(c, result, m)
elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags)
return semConv(c, n)
else:
result = overloadedCallOpr(c, n) # this uses efNoUndeclared
# Now that nkSym does not imply an iteration over the proc/iterator space,
@@ -1058,17 +1077,17 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
return result
#result = afterCallActions(c, result, nOrig, flags)
if result[0].kind == nkSym:
result = afterCallActions(c, result, nOrig, flags)
result = afterCallActions(c, result, nOrig, flags, expectedType)
else:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
# this seems to be a hotspot in the compiler!
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
else: result = errorNode(c, n)
proc buildEchoStmt(c: PContext, n: PNode): PNode =
@@ -1641,11 +1660,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
else:
discard
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = semSubscript(c, n, flags)
if result == nil:
# overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags)
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags, expectedType)
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(c, a[1], a)
@@ -1819,7 +1838,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
renderTree(a, {renderNoComments}))
else:
let lhs = n[0]
let rhs = semExprWithType(c, n[1], {})
let rhs = semExprWithType(c, n[1], {}, le)
if lhs.kind == nkSym and lhs.sym.kind == skResult:
n.typ = c.enforceVoidContext
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
@@ -1870,12 +1889,12 @@ proc semReturn(c: PContext, n: PNode): PNode =
else:
localError(c.config, n.info, "'return' not allowed here")
proc semProcBody(c: PContext, n: PNode): PNode =
proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
when defined(nimsuggest):
if c.graph.config.expandDone():
return n
openScope(c)
result = semExpr(c, n)
result = semExpr(c, n, expectedType = expectedType)
if c.p.resultSym != nil and not isEmptyType(result.typ):
if result.kind == nkNilLit:
# or ImplicitlyDiscardable(result):
@@ -2294,7 +2313,7 @@ proc semSizeof(c: PContext, n: PNode): PNode =
n.typ = getSysType(c.graph, n.info, tyInt)
result = foldSizeOf(c.config, n, n)
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
# this is a hotspot in the compiler!
result = n
case s.magic # magics that need special treatment
@@ -2410,8 +2429,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf:
markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s))
of mArrToSeq, mOpenArrayToSeq:
if n.len == 2 and expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tySequence, tyOpenArray}):
# seq type inference
var arrayType = newType(tyOpenArray, nextTypeId(c.idgen), expected.owner)
arrayType.rawAddSon(expected[0])
n[1] = semExpr(c, n[1], flags, arrayType)
result = semDirectOp(c, n, flags, expectedType)
else:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
# If semCheck is set to false, ``when`` will return the verbatim AST of
@@ -2473,33 +2501,49 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
if n.len == 1:
result.add(newTree(nkElse, newNode(nkStmtList)))
proc semSetConstr(c: PContext, n: PNode): PNode =
proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
result = newNodeI(nkCurly, n.info)
result.typ = newTypeS(tySet, c)
result.typ.flags.incl tfIsConstructor
var expectedElementType: PType = nil
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind == tySet):
expectedElementType = expected[0]
if n.len == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c))
rawAddSon(result.typ,
if expectedElementType != nil and
typeAllowed(expectedElementType, skLet, c) == nil:
expectedElementType
else:
newTypeS(tyEmpty, c))
else:
# only semantic checking for all elements, later type checking:
var typ: PType = nil
for i in 0..<n.len:
if isRange(n[i]):
checkSonsLen(n[i], 3, c.config)
n[i][1] = semExprWithType(c, n[i][1])
n[i][2] = semExprWithType(c, n[i][2])
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElementType)
n[i][2] = semExprWithType(c, n[i][2], {}, expectedElementType)
if typ == nil:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if expectedElementType == nil:
expectedElementType = typ
n[i].typ = n[i][2].typ # range node needs type too
elif n[i].kind == nkRange:
# already semchecked
if typ == nil:
typ = skipTypes(n[i][0].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if expectedElementType == nil:
expectedElementType = typ
else:
n[i] = semExprWithType(c, n[i])
n[i] = semExprWithType(c, n[i], {}, expectedElementType)
if typ == nil:
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if expectedElementType == nil:
expectedElementType = typ
if not isOrdinalType(typ, allowEnumWithHoles=true):
localError(c.config, n.info, errOrdinalTypeExpected)
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
@@ -2518,7 +2562,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
m = fitNode(c, typ, n[i], info)
result.add m
proc semTableConstr(c: PContext, n: PNode): PNode =
proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
# we simply transform ``{key: value, key2, key3: value}`` to
# ``[(key, value), (key2, value2), (key3, value2)]``
result = newNodeI(nkBracket, n.info)
@@ -2540,7 +2584,7 @@ proc semTableConstr(c: PContext, n: PNode): PNode =
lastKey = i+1
if lastKey != n.len: illFormedAst(n, c.config)
result = semExpr(c, result)
result = semExpr(c, result, expectedType = expectedType)
type
TParKind = enum
@@ -2567,8 +2611,13 @@ proc checkPar(c: PContext; n: PNode): TParKind =
localError(c.config, n[i].info, errNamedExprNotAllowed)
return paNone
proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = newNodeI(nkTupleConstr, n.info)
var expected: PType = nil
if expectedType != nil:
expected = expectedType.skipTypes(abstractRange-{tyDistinct})
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c)
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
var ids = initIntSet()
@@ -2578,7 +2627,9 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
let id = considerQuotedIdent(c, n[i][0])
if containsOrIncl(ids, id.id):
localError(c.config, n[i].info, errFieldInitTwice % id.s)
n[i][1] = semExprWithType(c, n[i][1], {})
# can check if field name matches expected type here
let expectedElemType = if expected != nil: expected[i] else: nil
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
if n[i][1].typ.kind == tyTypeDesc:
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
@@ -2593,18 +2644,24 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.add n[i]
result.typ = typ
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = n # we don't modify n, but compute the type:
result.transitionSonsKind(nkTupleConstr)
var expected: PType = nil
if expectedType != nil:
expected = expectedType.skipTypes(abstractRange-{tyDistinct})
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
for i in 0..<n.len:
n[i] = semExprWithType(c, n[i], {})
let expectedElemType = if expected != nil: expected[i] else: nil
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
addSonSkipIntLit(typ, n[i].typ, c.idgen)
result.typ = typ
include semobjconstr
proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
result = n
inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2, c.config)
@@ -2619,7 +2676,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
suggestSym(c.graph, n[0].info, labl, c.graph.usageSym)
styleCheckDef(c, labl)
onDef(n[0].info, labl)
n[1] = semExpr(c, n[1], flags)
n[1] = semExpr(c, n[1], flags, expectedType)
n.typ = n[1].typ
if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
else: n.transitionSonsKind(nkBlockExpr)
@@ -2685,8 +2742,8 @@ proc semExport(c: PContext, n: PNode): PNode =
s = nextOverloadIter(o, c, a)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var tupexp = semTuplePositionsConstr(c, n, flags)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
var isTupleType: bool
if tupexp.len > 0: # don't interpret () as type
isTupleType = tupexp[0].typ.kind == tyTypeDesc
@@ -2811,7 +2868,7 @@ proc semPragmaStmt(c: PContext; n: PNode) =
else:
pragma(c, c.p.owner, n, stmtPragmas, true)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
when defined(nimCompilerStacktraceHints):
setFrameMsg c.config$n.info & " " & $n.kind
when false: # see `tdebugutils`
@@ -2820,6 +2877,16 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
defer:
if isCompilerDebug():
echo ("<", c.config$n.info, n, ?.result.typ)
template directLiteral(typeKind: TTypeKind) =
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind == typeKind):
result.typ = expected
changeType(c, result, expectedType, check=true)
else:
result.typ = getSysType(c.graph, n.info, typeKind)
result = n
when defined(nimsuggest):
@@ -2839,13 +2906,23 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if nfSem in n.flags: return
case n.kind
of nkIdent, nkAccQuoted:
let checks = if efNoEvaluateGeneric in flags:
{checkUndeclared, checkPureEnumFields}
elif efInCall in flags:
{checkUndeclared, checkModule, checkPureEnumFields}
else:
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
var s = qualifiedLookUp(c, n, checks)
var s: PSym
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind == tyEnum):
let nameId = considerQuotedIdent(c, n).id
for f in expected.n:
if f.kind == nkSym and f.sym.name.id == nameId:
s = f.sym
break
if s == nil:
let checks = if efNoEvaluateGeneric in flags:
{checkUndeclared, checkPureEnumFields}
elif efInCall in flags:
{checkUndeclared, checkModule, checkPureEnumFields}
else:
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
s = qualifiedLookUp(c, n, checks)
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
case s.kind
of skProc, skFunc, skMethod, skConverter, skIterator:
@@ -2865,6 +2942,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semSym(c, n, s, flags)
else:
result = semSym(c, n, s, flags)
if expectedType != nil and isSymChoice(result):
result = fitNode(c, expectedType, result, n.info)
if result.kind == nkSym:
result = semSym(c, result, result.sym, flags)
of nkSym:
# because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again!
@@ -2872,39 +2953,56 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkEmpty, nkNone, nkCommentStmt, nkType:
discard
of nkNilLit:
if result.typ == nil: result.typ = getNilType(c)
if result.typ == nil:
result.typ = getNilType(c)
if expectedType != nil:
var m = newCandidate(c, result.typ)
if typeRel(m, expectedType, result.typ) >= isSubtype:
result.typ = expectedType
# or: result = fitNode(c, expectedType, result, n.info)
of nkIntLit:
if result.typ == nil: setIntLitType(c, result)
of nkInt8Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8)
of nkInt16Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16)
of nkInt32Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32)
of nkInt64Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64)
of nkUIntLit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt)
of nkUInt8Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8)
of nkUInt16Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16)
of nkUInt32Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32)
of nkUInt64Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64)
#of nkFloatLit:
# if result.typ == nil: result.typ = getFloatLitType(result)
of nkFloat32Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32)
of nkFloat64Lit, nkFloatLit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64)
of nkFloat128Lit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128)
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyInt..tyInt64,
tyUInt..tyUInt64,
tyFloat..tyFloat128}):
result.typ = expected
if expected.kind in {tyFloat..tyFloat128}:
n.transitionIntToFloatKind(nkFloatLit)
changeType(c, result, expectedType, check=true)
else:
setIntLitType(c, result)
of nkInt8Lit: directLiteral(tyInt8)
of nkInt16Lit: directLiteral(tyInt16)
of nkInt32Lit: directLiteral(tyInt32)
of nkInt64Lit: directLiteral(tyInt64)
of nkUIntLit: directLiteral(tyUInt)
of nkUInt8Lit: directLiteral(tyUInt8)
of nkUInt16Lit: directLiteral(tyUInt16)
of nkUInt32Lit: directLiteral(tyUInt32)
of nkUInt64Lit: directLiteral(tyUInt64)
of nkFloatLit:
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyFloat..tyFloat128}):
result.typ = expected
changeType(c, result, expectedType, check=true)
else:
result.typ = getSysType(c.graph, n.info, tyFloat64)
of nkFloat32Lit: directLiteral(tyFloat32)
of nkFloat64Lit: directLiteral(tyFloat64)
of nkFloat128Lit: directLiteral(tyFloat128)
of nkStrLit..nkTripleStrLit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyString)
of nkCharLit:
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar)
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyString, tyCstring}):
result.typ = expectedType
else:
result.typ = getSysType(c.graph, n.info, tyString)
of nkCharLit: directLiteral(tyChar)
of nkDotExpr:
result = semFieldAccess(c, n, flags)
if result.kind == nkDotCall:
@@ -2912,7 +3010,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, result, flags)
of nkBind:
message(c.config, n.info, warnDeprecated, "bind is deprecated")
result = semExpr(c, n[0], flags)
result = semExpr(c, n[0], flags, expectedType)
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
if c.matchedConcept != nil and n.len == 1:
let modifier = n.modifierTypeKindOfNode
@@ -2938,40 +3036,40 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# pretty.checkUse(n[0][1].info, s)
case s.kind
of skMacro, skTemplate:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
of skType:
# XXX think about this more (``set`` procs)
let ambig = c.isAmbiguous
if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
result = semConv(c, n)
result = semConv(c, n, expectedType)
elif ambig and n.len == 1:
errorUseQualifier(c, n.info, s)
elif n.len == 1:
result = semObjConstr(c, n, flags)
elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
result = semObjConstr(c, n, flags, expectedType)
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)
else: result = semMagic(c, n, s, flags)
else: result = semMagic(c, n, s, flags, expectedType)
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags)
result = semIndirectOp(c, n, flags, expectedType)
elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
isSymChoice(n[0][0]):
# indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n[0])
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
elif nfDotField in n.flags:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
elif isSymChoice(n[0]):
let b = asBracketExpr(c, n)
if b != nil:
result = semExpr(c, b, flags)
result = semExpr(c, b, flags, expectedType)
else:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
else:
result = semIndirectOp(c, n, flags)
result = semIndirectOp(c, n, flags, expectedType)
if nfDefaultRefsParam in result.flags:
result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?)
@@ -2990,12 +3088,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# This is a "when nimvm" stmt.
result = semWhen(c, n, true)
else:
result = semExpr(c, result, flags)
result = semExpr(c, result, flags, expectedType)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
result = semArrayAccess(c, n, flags)
result = semArrayAccess(c, n, flags, expectedType)
of nkCurlyExpr:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags)
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType)
of nkPragmaExpr:
var
pragma = n[1]
@@ -3017,12 +3115,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkPar, nkTupleConstr:
case checkPar(c, n)
of paNone: result = errorNode(c, n)
of paTuplePositions: result = semTupleConstr(c, n, flags)
of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
of paSingle: result = semExpr(c, n[0], flags)
of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags)
of nkObjConstr: result = semObjConstr(c, n, flags)
of paTuplePositions: result = semTupleConstr(c, n, flags, expectedType)
of paTupleFields: result = semTupleFieldsConstr(c, n, flags, expectedType)
of paSingle: result = semExpr(c, n[0], flags, expectedType)
of nkCurly: result = semSetConstr(c, n, expectedType)
of nkBracket: result = semArrayConstr(c, n, flags, expectedType)
of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
of nkDerefExpr: result = semDeref(c, n)
of nkAddr:
@@ -3032,9 +3130,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.typ = makePtrType(c, result[0].typ)
of nkHiddenAddr, nkHiddenDeref:
checkSonsLen(n, 1, c.config)
n[0] = semExpr(c, n[0], flags)
n[0] = semExpr(c, n[0], flags, expectedType)
of nkCast: result = semCast(c, n)
of nkIfExpr, nkIfStmt: result = semIf(c, n, flags)
of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType)
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
checkSonsLen(n, 2, c.config)
considerGenSyms(c, n)
@@ -3048,15 +3146,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
checkMinSonsLen(n, 2, c.config)
considerGenSyms(c, n)
of nkTableConstr:
result = semTableConstr(c, n)
result = semTableConstr(c, n, expectedType)
of nkClosedSymChoice, nkOpenSymChoice:
# handling of sym choices is context dependent
# the node is left intact for now
discard
of nkStaticExpr: result = semStaticExpr(c, n[0])
of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType)
of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet)
@@ -3064,10 +3162,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkTypeSection: result = semTypeSection(c, n)
of nkDiscardStmt: result = semDiscard(c, n)
of nkWhileStmt: result = semWhile(c, n, flags)
of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags)
of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags, expectedType)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
of nkCaseStmt: result = semCase(c, n, flags)
of nkCaseStmt: result = semCase(c, n, flags, expectedType)
of nkReturnStmt: result = semReturn(c, n)
of nkUsingStmt: result = semUsing(c, n)
of nkAsmStmt: result = semAsm(c, n)
@@ -3106,7 +3204,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
result = semExportExcept(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
result = semPragmaBlock(c, n, expectedType)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkDefer:

View File

@@ -77,7 +77,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
"the field '$1' is not accessible." % [field.name.s])
return
var initValue = semExprFlagDispatched(c, assignment[1], flags)
var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ)
if initValue != nil:
initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info)
assignment[0] = newSymNode(field)
@@ -375,13 +375,19 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
else:
assert false, "Must not enter here."
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
var t = semTypeNode(c, n[0], nil)
result = newNodeIT(nkObjConstr, n.info, t)
for child in n: result.add child
if t == nil:
return localErrorNode(c, result, "object constructor needs an object type")
if t.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
expectedType != nil and expectedType.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
t = expectedType
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
if t.kind == tyRef:

View File

@@ -98,7 +98,7 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
checkSonsLen(n, 2, c.config)
openScope(c)
n[0] = forceBool(c, semExprWithType(c, n[0]))
n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool)))
inc(c.p.nestedLoopCounter)
n[1] = semStmt(c, n[1], flags)
dec(c.p.nestedLoopCounter)
@@ -112,15 +112,15 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags)
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}; expectedType: PType = nil): PNode =
result = semExpr(c, n, flags, expectedType)
if result.typ != nil:
# XXX tyGenericInst here?
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode =
proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode =
openScope(c)
result = semExprBranch(c, n)
result = semExprBranch(c, n, expectedType = expectedType)
closeScope(c)
const
@@ -169,22 +169,25 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
s.add "; for a function call use ()"
localError(c.config, n.info, s)
proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
result = n
var typ = commonTypeBegin
var expectedType = expectedType
var hasElse = false
for i in 0..<n.len:
var it = n[i]
if it.len == 2:
openScope(c)
it[0] = forceBool(c, semExprWithType(c, it[0]))
it[1] = semExprBranch(c, it[1], flags)
it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool)))
it[1] = semExprBranch(c, it[1], flags, expectedType)
typ = commonType(c, typ, it[1])
expectedType = typ
closeScope(c)
elif it.len == 1:
hasElse = true
it[0] = semExprBranchScope(c, it[0])
it[0] = semExprBranchScope(c, it[0], expectedType)
typ = commonType(c, typ, it[0])
expectedType = typ
else: illFormedAst(it, c.config)
if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
(not hasElse and efInTypeof notin flags):
@@ -200,7 +203,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
result.transitionSonsKind(nkIfExpr)
result.typ = typ
proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
var check = initIntSet()
template semExceptBranchType(typeNode: PNode): bool =
# returns true if exception type is imported type
@@ -222,8 +225,10 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
checkMinSonsLen(n, 2, c.config)
var typ = commonTypeBegin
n[0] = semExprBranchScope(c, n[0])
var expectedType = expectedType
n[0] = semExprBranchScope(c, n[0], expectedType)
typ = commonType(c, typ, n[0].typ)
expectedType = typ
var last = n.len - 1
var catchAllExcepts = 0
@@ -281,9 +286,13 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions")
# last child of an nkExcept/nkFinally branch is a statement:
a[^1] = semExprBranchScope(c, a[^1])
if a.kind != nkFinally: typ = commonType(c, typ, a[^1])
else: dec last
if a.kind != nkFinally:
a[^1] = semExprBranchScope(c, a[^1], expectedType)
typ = commonType(c, typ, a[^1])
expectedType = typ
else:
a[^1] = semExprBranchScope(c, a[^1])
dec last
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped}:
@@ -563,7 +572,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var def: PNode = c.graph.emptyNode
if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {})
def = semExprWithType(c, a[^1], {}, typ)
if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum:
errorSymChoiceUseQualifier(c, def)
@@ -717,7 +726,7 @@ proc semConst(c: PContext, n: PNode): PNode =
var typFlags: TTypeAllowedFlags
# don't evaluate here since the type compatibility check below may add a converter
var def = semExprWithType(c, a[^1])
var def = semExprWithType(c, a[^1], {}, typ)
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
typFlags.incl taIsTemplateOrMacro
@@ -1008,7 +1017,7 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
result.typ = result.lastSon.typ
closeScope(c)
proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
result = n
checkMinSonsLen(n, 2, c.config)
openScope(c)
@@ -1017,6 +1026,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
var chckCovered = false
var covered: Int128 = toInt128(0)
var typ = commonTypeBegin
var expectedType = expectedType
var hasElse = false
let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc})
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
@@ -1048,20 +1058,23 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
checkMinSonsLen(x, 2, c.config)
semCaseBranch(c, n, x, i, covered)
var last = x.len-1
x[last] = semExprBranchScope(c, x[last])
x[last] = semExprBranchScope(c, x[last], expectedType)
typ = commonType(c, typ, x[last])
expectedType = typ
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2, c.config)
openScope(c)
x[0] = forceBool(c, semExprWithType(c, x[0]))
x[1] = semExprBranch(c, x[1])
x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool)))
x[1] = semExprBranch(c, x[1], expectedType = expectedType)
typ = commonType(c, typ, x[1])
expectedType = typ
closeScope(c)
of nkElse:
checkSonsLen(x, 1, c.config)
x[0] = semExprBranchScope(c, x[0])
x[0] = semExprBranchScope(c, x[0], expectedType)
typ = commonType(c, typ, x[0])
expectedType = typ
if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse:
message(c.config, x.info, warnUnreachableElse)
hasElse = true
@@ -1670,7 +1683,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
addParams(c, params, skProc)
pushProcCon(c, s)
addResult(c, n, n.typ[0], skProc)
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0]))
trackProc(c, s, s.ast[bodyPos])
popProcCon(c)
popOwner(c)
@@ -2092,7 +2105,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# allowed, everything else, including a nullary generic is an error.
pushProcCon(c, s)
addResult(c, n, s.typ[0], skProc)
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ[0]))
trackProc(c, s, s.ast[bodyPos])
popProcCon(c)
elif efOperand notin flags:
@@ -2105,8 +2118,15 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
maybeAddResult(c, s, n)
let resultType =
if s.kind == skMacro:
sysTypeFromName(c.graph, n.info, "NimNode")
elif not isInlineIterator(s.typ):
s.typ[0]
else:
nil
# semantic checking also needed with importc in case used in VM
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
# unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles':
trackProc(c, s, s.ast[bodyPos])
@@ -2280,7 +2300,7 @@ proc setLine(n: PNode, info: TLineInfo) =
for i in 0..<n.safeLen: setLine(n[i], info)
n.info = info
proc semPragmaBlock(c: PContext, n: PNode): PNode =
proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
checkSonsLen(n, 2, c.config)
let pragmaList = n[0]
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
@@ -2297,7 +2317,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
localError(c.config, p.info, "invalid pragma block: " & $p)
inc c.inUncheckedAssignSection, inUncheckedAssignSection
n[1] = semExpr(c, n[1])
n[1] = semExpr(c, n[1], expectedType = expectedType)
dec c.inUncheckedAssignSection, inUncheckedAssignSection
result = n
result.typ = n[1].typ
@@ -2346,7 +2366,7 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
"attempt to equate '%s' and '%s'." % [inferred.renderTree, $res.typ, $typ.base])
typ.n = res
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode =
result = n
result.transitionSonsKind(nkStmtList)
var voidContext = false
@@ -2359,7 +2379,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# nkNilLit, nkEmpty}:
# dec last
for i in 0..<n.len:
var x = semExpr(c, n[i], flags)
var x = semExpr(c, n[i], flags, if i == n.len - 1: expectedType else: nil)
n[i] = x
if c.matchedConcept != nil and x.typ != nil and
(nfFromTemplate notin n.flags or i != last):

View File

@@ -454,9 +454,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
setSize typ, 1
of tyInt16, tyUInt16:
setSize typ, 2
of tyInt32, tyUInt32:
of tyInt32, tyUInt32, tyFloat32:
setSize typ, 4
of tyInt64, tyUInt64:
of tyInt64, tyUInt64, tyFloat64, tyFloat:
setSize typ, 8
else:
typ.size = szUnknownSize

View File

@@ -491,7 +491,9 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
# this list is subtle, we try to answer the question if after 'dest = f(src)'
# there is a connection betwen 'src' and 'dest' so that mutations to 'src'
# also reflect 'dest':
if magic in {mNone, mMove, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mArrToSeq}:
if magic in {mNone, mMove, mSlice,
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mArrToSeq, mOpenArrayToSeq}:
for i in 1..<n.len:
# we always have to assume a 'select(...)' like mechanism.
# But at least we do filter out simple POD types from the

View File

@@ -1038,7 +1038,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
c.genAsgnPatch(n[1], d)
c.freeTemp(d)
of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest)
of mIsolate, mFinished:
of generatedMagics:
genCall(c, n, dest)
of mNew, mNewFinalize:
unused(c, n, dest)

View File

@@ -123,6 +123,65 @@ This feature has to be enabled via `{.experimental: "implicitDeref".}`:
echo n.depth
# no need to write n[].depth either
Top-down type inference
=======================
In expressions such as:
```nim
let a: T = ex
```
Normally, the compiler type checks the expression `ex` by itself, then
attempts to statically convert the type-checked expression to the given type
`T` as much as it can, while making sure it matches the type. The extent of
this process is limited however due to the expression usually having
an assumed type that might clash with the given type.
With top-down type inference, the expression is type checked with the
extra knowledge that it is supposed to be of type `T`. For example,
the following code is does not compile with the former method, but
compiles with top-down type inference:
```nim
let foo: (float, uint8, cstring) = (1, 2, "abc")
```
The tuple expression has an expected type of `(float, uint8, cstring)`.
Since it is a tuple literal, we can use this information to assume the types
of its elements. The expected types for the expressions `1`, `2` and `"abc"`
are respectively `float`, `uint8`, and `cstring`; and these expressions can be
statically converted to these types.
Without this information, the type of the tuple expression would have been
assumed to be `(int, int, string)`. Thus the type of the tuple expression
would not match the type of the variable, and an error would be given.
The extent of this varies, but there are some notable special cases.
Sequence literals
-----------------
Top-down type inference applies to sequence literals.
```nim
let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]]
```
This behavior is tied to the `@` overloads in the `system` module,
so overloading `@` can disable this behavior. This can be circumvented by
specifying the `` system.`@` `` overload.
```nim
proc `@`(x: string): string = "@" & x
# does not compile:
let x: seq[float] = @[1, 2, 3]
# compiles:
let x: seq[float] = system.`@`([1, 2, 3])
```
Code reordering
===============

View File

@@ -787,7 +787,7 @@ template toSeq1(s: not iterator): untyped =
i += 1
result
else:
var result: seq[OutType] = @[]
var result: seq[OutType]# = @[]
for it in s:
result.add(it)
result
@@ -804,7 +804,7 @@ template toSeq2(iter: iterator): untyped =
result
else:
type OutType = typeof(iter2())
var result: seq[OutType] = @[]
var result: seq[OutType]# = @[]
when compiles(iter2()):
evalOnceAs(iter4, iter, false)
let iter3 = iter4()
@@ -848,7 +848,7 @@ template toSeq*(iter: untyped): untyped =
inc i
result
else:
var result: seq[typeof(iter)] = @[]
var result: seq[typeof(iter)]# = @[]
for x in iter:
result.add(x)
result
@@ -1016,7 +1016,7 @@ template mapIt*(s: typed, op: untyped): untyped =
i += 1
result
else:
var result: seq[OutType] = @[]
var result: seq[OutType]# = @[]
# use `items` to avoid https://github.com/nim-lang/Nim/issues/12639
for it {.inject.} in items(s):
result.add(op)

View File

@@ -1624,13 +1624,23 @@ proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".}
## `== nil`.
proc `@`*[T](a: openArray[T]): seq[T] =
## Turns an *openArray* into a sequence.
##
## This is not as efficient as turning a fixed length array into a sequence
## as it always copies every element of `a`.
newSeq(result, a.len)
for i in 0..a.len-1: result[i] = a[i]
when defined(nimHasTopDownInference):
# magic used for seq type inference
proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} =
## Turns an *openArray* into a sequence.
##
## This is not as efficient as turning a fixed length array into a sequence
## as it always copies every element of `a`.
newSeq(result, a.len)
for i in 0..a.len-1: result[i] = a[i]
else:
proc `@`*[T](a: openArray[T]): seq[T] =
## Turns an *openArray* into a sequence.
##
## This is not as efficient as turning a fixed length array into a sequence
## as it always copies every element of `a`.
newSeq(result, a.len)
for i in 0..a.len-1: result[i] = a[i]
when defined(nimSeqsV2):

View File

@@ -453,8 +453,8 @@ block:
for i in 0..<len:
yield i
# xxx: obscure CT error: basic_types.nim(16, 16) Error: internal error: symbol has no generated name: true
when not defined(js):
# xxx: obscure CT error: basic_types.nim(16, 16) Error: internal error: symbol has no generated name: true
doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6
block: # strictFuncs tests with ref object

View File

@@ -0,0 +1,195 @@
block:
var s: seq[string] = (discard; @[])
var x: set[char] =
if true:
try:
case 1
of 1:
if false:
{'4'}
else:
block:
s.add "a"
{}
else: {'3'}
except: {'2'}
else: {'1'}
doAssert x is set[char]
doAssert x == {}
doAssert s == @["a"]
x = {'a', 'b'}
doAssert x == {'a', 'b'}
x = (s.add "b"; {})
doAssert x == {}
doAssert s == @["a", "b"]
let x2: set[byte] = {1}
doAssert x2 == {1u8}
block:
let x3: array[0..2, byte] = [1, 2, 3]
#let x4: openarray[byte] = [1, 2, 3]
#let x5: openarray[byte] = @[1, 2, 3]
let x6: seq[byte] = @[1, 2, 3]
let x7: seq[seq[float32]] = @[@[1, 2, 3], @[4.3, 5, 6]]
type ABC = enum a, b, c
let x8: array[ABC, byte] = [1, 2, 3]
doAssert x8[a] == 1
doAssert x8[a] + x8[b] == x8[c]
const x9: array[-2..2, float] = [0, 1, 2, 3, 4]
let x10: array[ABC, byte] = block:
{.gcsafe.}:
[a: 1, b: 2, c: 3]
proc `@`(x: float): float = x + 1
doAssert @1 == 2
let x11: seq[byte] = system.`@`([1, 2, 3])
block:
type Foo = object
x: BiggestInt
var foo: Foo
foo.x = case true
of true: ord(1)
else: 0
foo.x = if true: ord(1) else: 0
block:
type Foo = object
x: (float, seq[(byte, seq[byte])])
let foo = Foo(x: (1, @{2: @[], 3: @[4, 5]}))
doAssert foo.x == (1.0, @{2u8: @[], 3u8: @[4u8, 5]})
block:
type Foo = object
x: tuple[a: float, b: seq[(byte, seq[byte])]]
let foo = Foo(x: (a: 1, b: @{2: @[3, 4], 5: @[]}))
doAssert foo.x == (1.0, @{2u8: @[3u8, 4], 5u8: @[]})
block:
proc foo(): seq[float] = @[1]
let fooLamb = proc(): seq[float] = @[1]
doAssert foo() == fooLamb()
block:
type Foo[T] = float32
let x: seq[Foo[int32]] = @[1]
block:
type Foo = ref object
type Bar[T] = ptr object
let x1: seq[Foo] = @[nil]
let x2: seq[Bar[int]] = @[nil]
let x3: seq[cstring] = @[nil]
block:
let x: seq[cstring] = @["abc", nil, "def"]
doAssert x.len == 3
doAssert x[0] == cstring"abc"
doAssert x[1].isNil
doAssert x[2] == "def".cstring
block:
type Foo = object
x: tuple[a: float, b: seq[(byte, seq[cstring])]]
let foo = Foo(x: (a: 1, b: @{2: @[nil, "abc"]}))
doAssert foo.x == (1.0, @{2u8: @[cstring nil, cstring "abc"]})
block:
type Foo = object
x: tuple[a: float, b: seq[(byte, seq[ptr int])]]
let foo = Foo(x: (a: 1, b: @{2: @[nil, nil]}))
doAssert foo.x == (1.0, @{2u8: @[(ptr int)(nil), nil]})
when false: # unsupported
block: # type conversion
let x = seq[(cstring, float32)](@{"abc": 1.0, "def": 2.0})
doAssert x[0] == (cstring"abc", 1.0'f32)
doAssert x[1] == (cstring"def", 2.0'f32)
block: # enum
type Foo {.pure.} = enum a
type Bar {.pure.} = enum a, b, c
var s: seq[Bar] = @[a, b, c]
block: # overload selection
proc foo(x, y: int): int = x + y + 1
proc foo(x: int): int = x - 1
var s: seq[proc (x, y: int): int] = @[nil, foo, foo]
var s2: seq[int]
for a in s:
if not a.isNil: s2.add(a(1, 2))
doAssert s2 == @[4, 4]
block: # with generics?
proc foo(x, y: int): int = x + y + 1
proc foo(x: int): int = x - 1
proc bar[T](x, y: T): T = x - y
var s: seq[proc (x, y: int): int] = @[nil, foo, foo, bar]
var s2: seq[int]
for a in s:
if not a.isNil: s2.add(a(1, 2))
doAssert s2 == @[4, 4, -1]
proc foo(x, y: float): float = x + y + 1.0
var s3: seq[proc (x, y: float): float] = @[nil, foo, foo, bar]
var s4: seq[float]
for a in s3:
if not a.isNil: s4.add(a(1, 2))
doAssert s4 == @[4.0, 4, -1]
block: # range types
block:
let x: set[range[1u8..5u8]] = {1, 3}
doAssert x == {range[1u8..5u8](1), 3}
doAssert $x == "{1, 3}"
block:
let x: seq[set[range[1u8..5u8]]] = @[{1, 3}]
doAssert x == @[{range[1u8..5u8](1), 3}]
doAssert $x[0] == "{1, 3}"
block:
let x: seq[range[1u8..5u8]] = @[1, 3]
doAssert x == @[range[1u8..5u8](1), 3]
doAssert $x == "@[1, 3]"
block: # already worked before, make sure it still works
let x: set[range['a'..'e']] = {'a', 'c'}
doAssert x == {range['a'..'e']('a'), 'c'}
doAssert $x == "{'a', 'c'}"
block: # extended
let x: seq[set[range['a'..'e']]] = @[{'a', 'c'}]
doAssert x[0] == {range['a'..'e']('a'), 'c'}
doAssert $x == "@[{'a', 'c'}]"
block:
type Foo = object
x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[range['a'..'e']])])
let foo = Foo(x: (1, @{2: @[], 3: @['c', 'd']}))
doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[range['a'..'e']('c'), 'd']})
block:
type Foo = object
x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[set[range['a'..'e']]])])
let foo = Foo(x: (1, @{2: @[], 3: @[{'c', 'd'}]}))
doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[{range['a'..'e']('c'), 'd'}]})
block: # templates
template foo: untyped = (1, 2, "abc")
let x: (float, byte, cstring) = foo()
doAssert x[0] == float(1)
doAssert x[1] == byte(2)
doAssert x[2] == cstring("abc")
let (a, b, c) = x
doAssert a == float(1)
doAssert b == byte(2)
doAssert c == cstring("abc")