diff options
author | metagn <metagngn@gmail.com> | 2022-08-24 08:11:41 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-24 07:11:41 +0200 |
commit | 0014b9c48e883d3c04995b9e83bb0f8468a16df6 (patch) | |
tree | ddaffc384999eea35bbab72cc4265d01ec18fc17 | |
parent | 2dcfd732609a2cfa805e5a94cc105399a2f18632 (diff) | |
download | Nim-0014b9c48e883d3c04995b9e83bb0f8468a16df6.tar.gz |
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>
-rw-r--r-- | compiler/ast.nim | 11 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 5 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/jsgen.nim | 6 | ||||
-rw-r--r-- | compiler/sem.nim | 44 | ||||
-rw-r--r-- | compiler/semdata.nim | 6 | ||||
-rw-r--r-- | compiler/semexprs.nim | 342 | ||||
-rw-r--r-- | compiler/semobjconstr.nim | 10 | ||||
-rw-r--r-- | compiler/semstmts.nim | 76 | ||||
-rw-r--r-- | compiler/sizealignoffsetimpl.nim | 4 | ||||
-rw-r--r-- | compiler/varpartitions.nim | 4 | ||||
-rw-r--r-- | compiler/vmgen.nim | 2 | ||||
-rw-r--r-- | doc/manual_experimental.md | 58 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 8 | ||||
-rw-r--r-- | lib/system.nim | 24 | ||||
-rw-r--r-- | tests/stdlib/tsequtils.nim | 2 | ||||
-rw-r--r-- | tests/types/ttopdowninference.nim | 195 |
17 files changed, 599 insertions, 199 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index c720a76fb..b84468167 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -674,7 +674,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, @@ -708,8 +708,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, @@ -738,6 +738,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 @@ -1677,6 +1680,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) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1d5ceb779..fb2f4aeaf 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2369,7 +2369,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) @@ -2978,7 +2978,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 diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4d4358b16..f1d16c4e4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -140,4 +140,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasEffectsOf") defineSymbol("nimHasEnforceNoRaises") + defineSymbol("nimHasTopDownInference") defineSymbol("nimHasTemplateRedefinitionPragma") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 60d10f58d..b30d03579 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1455,7 +1455,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: @@ -2106,6 +2106,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: @@ -2622,7 +2624,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) diff --git a/compiler/sem.nim b/compiler/sem.nim index 70c57864c..8fac158ba 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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: # 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) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 2b1f19f9e..363b7e5a4 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -120,10 +120,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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b47aff429..84d45b7df 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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) @@ -583,21 +584,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)) @@ -608,8 +624,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: @@ -621,13 +639,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, {}) @@ -853,10 +871,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 @@ -900,7 +918,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 = @@ -923,15 +941,15 @@ 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) result = n 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) @@ -947,7 +965,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] @@ -972,13 +990,17 @@ 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) - 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) @@ -1017,9 +1039,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) # Now that nkSym does not imply an iteration over the proc/iterator space, @@ -1038,17 +1057,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 = @@ -1594,11 +1613,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) @@ -1774,7 +1793,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): @@ -1823,9 +1842,9 @@ 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 = 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): @@ -2256,7 +2275,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 @@ -2372,8 +2391,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 @@ -2432,33 +2460,49 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = result = newNodeI(nkEmpty, n.info) if whenNimvm: result.typ = typ -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) @@ -2477,7 +2521,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) @@ -2499,7 +2543,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 @@ -2526,8 +2570,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() @@ -2537,7 +2586,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.") @@ -2552,18 +2603,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) @@ -2578,7 +2635,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) @@ -2644,8 +2701,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 @@ -2770,7 +2827,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` @@ -2779,19 +2836,39 @@ 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 if c.config.cmd == cmdIdeTools: suggestExpr(c, n) 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: @@ -2811,6 +2888,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! @@ -2818,39 +2899,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: @@ -2858,7 +2956,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..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy: if c.matchedConcept != nil and n.len == 1: let modifier = n.modifierTypeKindOfNode @@ -2884,40 +2982,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?) @@ -2936,12 +3034,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] @@ -2963,12 +3061,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: @@ -2978,9 +3076,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) @@ -2994,15 +3092,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: 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) @@ -3010,10 +3108,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) @@ -3052,7 +3150,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: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 6d11d321e..f3efc1719 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8b0691abf..43687cd2e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -86,7 +86,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) @@ -98,15 +98,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 @@ -152,22 +152,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): @@ -183,7 +186,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 @@ -203,8 +206,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 @@ -261,9 +266,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}: @@ -597,7 +606,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) @@ -753,7 +762,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 @@ -1039,7 +1048,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) @@ -1048,6 +1057,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} @@ -1079,20 +1089,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 @@ -1678,7 +1691,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) @@ -2100,7 +2113,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: @@ -2113,8 +2126,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]) @@ -2291,7 +2311,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) @@ -2308,7 +2328,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 @@ -2357,7 +2377,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 @@ -2370,7 +2390,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): diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index c2e97aa53..009acf6eb 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -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 diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index a93104b06..403aca38c 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -494,7 +494,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 diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index aa2bdbe4c..554b048e6 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1026,7 +1026,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) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index f584c8905..0e4b317fb 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -124,6 +124,64 @@ ambiguous, a static error will be produced. p value2 ``` +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]) +``` + Package level objects ===================== diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 39908e9c1..b86977539 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -791,7 +791,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 @@ -808,7 +808,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() @@ -852,7 +852,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 @@ -1020,7 +1020,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) diff --git a/lib/system.nim b/lib/system.nim index 041d456fb..d8d90f11a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1614,13 +1614,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): diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 179f619f0..ddc023434 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -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 diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim new file mode 100644 index 000000000..296729cd8 --- /dev/null +++ b/tests/types/ttopdowninference.nim @@ -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") |