summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2022-08-24 08:11:41 +0300
committerGitHub <noreply@github.com>2022-08-24 07:11:41 +0200
commit0014b9c48e883d3c04995b9e83bb0f8468a16df6 (patch)
treeddaffc384999eea35bbab72cc4265d01ec18fc17
parent2dcfd732609a2cfa805e5a94cc105399a2f18632 (diff)
downloadNim-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.nim11
-rw-r--r--compiler/ccgexprs.nim5
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/jsgen.nim6
-rw-r--r--compiler/sem.nim44
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim342
-rw-r--r--compiler/semobjconstr.nim10
-rw-r--r--compiler/semstmts.nim76
-rw-r--r--compiler/sizealignoffsetimpl.nim4
-rw-r--r--compiler/varpartitions.nim4
-rw-r--r--compiler/vmgen.nim2
-rw-r--r--doc/manual_experimental.md58
-rw-r--r--lib/pure/collections/sequtils.nim8
-rw-r--r--lib/system.nim24
-rw-r--r--tests/stdlib/tsequtils.nim2
-rw-r--r--tests/types/ttopdowninference.nim195
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")