From 35b0666baf5bce931b4bd5c1abdb14f0ef55bd9b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 28 Feb 2020 08:25:30 -0800 Subject: [PATCH] fix #13502 `a and b` now short-circuits semcheck in VM --- compiler/semexprs.nim | 19 +++++++++++++++++++ lib/system/basic_types.nim | 7 +++++++ tests/system/tsystem_misc.nim | 17 +++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f75642bcb9a83..7c8e203f8a716 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2153,6 +2153,25 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result[0] = newSymNode(s, n[0].info) result[1] = semAddrArg(c, n[1], s.name.s == "unsafeAddr") result.typ = makePtrType(c, result[1].typ) + of mBitandI: + # somehow, `true and true` falls here and not under mAnd, IIUC this is because + # magic overloads (`and`) don't do overload resolution since semcheck + # hasn't been done yet + if c.inStaticContext > 0: + let lhs = semExprWithType(c, n[1]) + if lhs.typ.kind == tyBool: + result = newTree(nkCall) + result.add newIdentNode(getIdent(c.cache, "nimInternalAndVM"), n.info) + result.add lhs + result.add n[2] + result = semExpr(c, result) + else: + let n2 = copyNode(n) + n2.sons = n.sons + n2[1] = lhs + result = semDirectOp(c, n2, flags) + else: + result = semDirectOp(c, n, flags) of mTypeOf: markUsed(c, n.info, s) result = semTypeOf(c, n) diff --git a/lib/system/basic_types.nim b/lib/system/basic_types.nim index a6bf69ebc2f5d..0cb40074e39bc 100644 --- a/lib/system/basic_types.nim +++ b/lib/system/basic_types.nim @@ -50,6 +50,13 @@ proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.} ## are true). ## ## Evaluation is lazy: if ``x`` is false, ``y`` will not even be evaluated. + ## Semantic check is lazy in VM, to allow `when declared(Foo) and T is Foo` + +template nimInternalAndVM*(x: bool, y: untyped): bool = + ## Internal lowering used by `and` in VM to enable shortcuiting semcheck + when x: y + else: false + proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.} ## Boolean ``or``; returns true if ``not (not x and not y)`` (if any of ## the arguments is true). diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index fff8b7022ef50..197b604ccab66 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -210,3 +210,20 @@ block: # Ordinal # doAssert enum is Ordinal # fails # doAssert Ordinal is SomeOrdinal # doAssert SomeOrdinal is Ordinal + +block: # and VM shortcircuits semcheck + doAssert not compiles(nonexistant) + const a1 = false and nonexistant + doAssert not a1 + const a2 = true and false + doAssert not a2 + const a3 = true and true + doAssert a3 + + static: + const a4 = false and nonexistant + doAssert not a4 + let a5 = false and nonexistant # still works, we're inside a static context + doAssert not a5 + const a6 = 2 and 3 # make sure we don't break bitwise and + doAssert a6 == 2