Skip to content

Commit

Permalink
Reimplement null coalescing operator in a bytecode-compatible way
Browse files Browse the repository at this point in the history
  • Loading branch information
Sainan authored and well-in-that-case committed May 20, 2024
1 parent 7e7a535 commit c058457
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 61 deletions.
56 changes: 25 additions & 31 deletions src/lcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ static int patchtestreg (FuncState *fs, int node, int reg) {
Instruction *i = getjumpcontrol(fs, node);
if (GET_OPCODE(*i) != OP_TESTSET)
return 0; /* cannot patch other instructions */
if ((reg != NO_REG && reg != GETARG_B(*i)) || GETARG_C(*i) == NULL_COALESCE) /* Prevent NULL_COALESCE from being turned into OP_TEST. */
if (reg != NO_REG && reg != GETARG_B(*i))
SETARG_A(*i, reg);
else {
/* no register to put value or register already has the value;
Expand Down Expand Up @@ -894,7 +894,7 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
** (Expression still may have jump lists.)
*/
static void discharge2anyreg (FuncState *fs, expdesc *e) {
if (e->k != VNONRELOC&& e->k != VSAFECALL) { /* no fixed register yet? */
if (e->k != VNONRELOC && e->k != VSAFECALL) { /* no fixed register yet? */
luaK_reserveregs(fs, 1); /* get a register */
discharge2reg(fs, e, fs->freereg-1); /* put value there */
}
Expand Down Expand Up @@ -1317,29 +1317,6 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) {
}



/*
** Emit code to go through if 'e' is nil, jump otherwise.
*/
void luaK_goifnil (FuncState *fs, expdesc *e) {
int pc; /* pc of new jump */
luaK_dischargevars(fs, e);
if (luaK_isalwaysnil(fs->ls, e, true)) {
pc = NO_JUMP; /* always nil; do nothing*/
}
else {
discharge2anyreg(fs, e);
freeexp(fs, e);
luaK_codeABCk(fs, OP_TESTSET, NO_REG, e->u.reg, NULL_COALESCE, 1);
fs->f->onPlutoOpUsed(0);
pc = luaK_jump(fs); /* jump if nil */
}
luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */
luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */
e->f = NO_JUMP;
}


/*
** Code 'not e', doing constant folding.
*/
Expand Down Expand Up @@ -1803,7 +1780,21 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
break;
}
case OPR_COAL: {
luaK_goifnil(fs, v);
int pc; /* pc of new jump */
const auto reg = luaK_exp2anyreg(fs, v);
if (luaK_isalwaysnil(fs->ls, v, true)) {
pc = NO_JUMP; /* always nil; do nothing*/
}
else {
luaK_infix(fs, OPR_NE, v);
expdesc nil;
nil.k = VNIL;
nil.t = nil.f = NO_JUMP;
luaK_posfix(fs, OPR_NE, v, &nil, fs->ls->getLineNumber());
lua_assert(v->k == VJMP);
pc = v->u.pc;
}
v->u.reg = reg;
break;
}
case OPR_CONCAT: {
Expand Down Expand Up @@ -1909,16 +1900,19 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
*e1 = *e2;
break;
}
case OPR_COAL: {
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
case OPR_COAL: {
if (e1->u.pc != NO_JUMP) {
luaK_exp2reg(fs, e2, e1->u.reg);
luaK_patchtohere(fs, e1->u.pc);
}
e1->k = VNONRELOC;
/* e1->u.reg already set up */
break;
}
case OPR_CONCAT: { /* e1 .. e2 */
Expand Down
1 change: 0 additions & 1 deletion src/lcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC bool luaK_isalwaysnil (LexState *ls, expdesc *e, bool jumps_are_ok = false);
LUAI_FUNC bool luaK_isalwaystrue (LexState *ls, expdesc *e, bool jumps_are_ok = false);
LUAI_FUNC bool luaK_isalwaysfalse (LexState *ls, expdesc *e, bool jumps_are_ok = false);
LUAI_FUNC void luaK_goifnil (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
Expand Down
5 changes: 0 additions & 5 deletions src/lopcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,6 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
*/
#define NO_REG MAXARG_A

/*
** Identifier used in R(C) for OP_TESTSET, to indicate whether 'false' should be falsy.
*/
#define NULL_COALESCE 2


/*
** R[x] - register
Expand Down
3 changes: 0 additions & 3 deletions src/lparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3675,7 +3675,6 @@ static BinOpr subexpr (LexState *ls, expdesc *v, int limit, TypeHint *prop, int
else {
if (luaK_isalwaystrue(ls, v) || luaK_isalwaysfalse(ls, v))
throw_warn(ls, "unreachable code", "the expression before the '?\?' is never nil, hence the expression after the '?\?' is never used.", WT_UNREACHABLE_CODE);
throw_warn(ls, "non-portable operator usage", "this operator generates bytecode which is incompatible with Lua.", WT_NON_PORTABLE_BYTECODE);
}
if (prop) {
prop->erase(VT_NIL);
Expand Down Expand Up @@ -3872,8 +3871,6 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
if (compound_op != OPR_NOBINOPR) { /* compound operator? */
if (l_unlikely(ls->t.seminfo.i == TK_POW))
throw_warn(ls, "'**' is deprecated", "use '^' instead", WT_DEPRECATED);
if (compound_op == OPR_COAL)
throw_warn(ls, "non-portable operator usage", "this operator generates bytecode which is incompatible with Lua.", WT_NON_PORTABLE_BYTECODE);
check_condition(ls, nvars == 1, "unsupported tuple assignment");
compoundassign(ls, &lh->v, compound_op); /* perform binop & assignment */
return; /* avoid default */
Expand Down
27 changes: 6 additions & 21 deletions src/lvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2217,32 +2217,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmDumpInit();
vmDumpAddA();
vmDumpAddB();
vmDumpAddC();
vmDumpAdd (GETARG_k(i));
StkId ra = RA(i);
TValue *rb = vRB(i);
if (GETARG_C(i) == NULL_COALESCE) { /* R(C) is used as an identifier, as it was previously unused. */
if (ttisnil(rb)) {
pc++;
vmDumpOut("; null coalesce, no assignment");
}
else {
setobj2s(L, ra, rb);
donextjump(ci);
vmDumpOut("; null coalesce, push/assign " << stringify_tvalue(rb));
}
if (l_isfalse(rb) == GETARG_k(i)) {
pc++;
vmDumpOut("; no assignment");
}
else {
if (l_isfalse(rb) == GETARG_k(i))
{
pc++;
vmDumpOut("; no assignment");
}
else {
setobj2s(L, ra, rb);
donextjump(ci);
vmDumpOut("; push/assign " << stringify_tvalue(rb));
}
setobj2s(L, ra, rb);
donextjump(ci);
vmDumpOut("; push/assign " << stringify_tvalue(rb));
}
vmbreak;
}
Expand Down
3 changes: 3 additions & 0 deletions testes/pluto/basic.pluto
Original file line number Diff line number Diff line change
Expand Up @@ -617,13 +617,16 @@ do
b = "hello"
c = a ?? b
assert(c == "hello")
assert(not (a ?? b) == false)
a = false
b = "hello"
a ??= b
assert(a == false)
assert(not (a ?? b) == true)
a = nil
a ??= b
assert(a == "hello")
assert(not (a ?? b) == false)
end

print "Testing safe navigation."
Expand Down

0 comments on commit c058457

Please sign in to comment.