wcc/ww: cgtryprop/cgtryunw gain the nullable arm
Try-propagation on a nullable value fell to the tagged path and compared the pointer to a tag — the null check came out inverted. Mirror ww's own cgtypetest nullable fold (cgenexpr.ww:652) and the cstage twin (cgen.c:10366/10504). Review item #15.
This commit is contained in:
@@ -23500,6 +23500,22 @@ fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
// cursor here (was a silent word0 unwrap); call sources keep
|
||||
// the plain cgexpr emission byte-for-byte.
|
||||
cgtryunwcursor(c, n, "?");
|
||||
// Nullable `(*T | void)`: AX IS the pointer, not a tag. Non-null
|
||||
// = success (any *T variant), null = error → propagate (RET with
|
||||
// AX=0). The pre-fix path compared AX against successtag and so
|
||||
// treated null as success (inverted). Mirrors cstage cgen.c
|
||||
// N_TRYPROP nullable arm; ww's own cgtypetest carries the twin
|
||||
// (cgenexpr.ww nullable fold). (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryprop_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBP, SP\n\tPOPQ\tBP\n\tRET\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
// AX = tag. If not the success tag, this is an error; pop frame and
|
||||
// RET. Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
@@ -23638,6 +23654,21 @@ fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Family C (#35/#46): see the cgtryprop twin.
|
||||
cgtryunwcursor(c, n, "!");
|
||||
// Nullable `(*T | void)`: AX IS the pointer. Non-null = success
|
||||
// (leave AX as-is), null = error → abort exit(1). The pre-fix
|
||||
// path compared AX against successtag, treating null as success
|
||||
// (inverted). Mirrors cstage cgen.c N_TRYUNW nullable arm; ww's
|
||||
// own cgtypetest carries the twin. (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryunw_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
let cl: str = mklabel(c, "tryunw_ok");
|
||||
// Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
|
||||
@@ -349,6 +349,22 @@ fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
// cursor here (was a silent word0 unwrap); call sources keep
|
||||
// the plain cgexpr emission byte-for-byte.
|
||||
cgtryunwcursor(c, n, "?");
|
||||
// Nullable `(*T | void)`: AX IS the pointer, not a tag. Non-null
|
||||
// = success (any *T variant), null = error → propagate (RET with
|
||||
// AX=0). The pre-fix path compared AX against successtag and so
|
||||
// treated null as success (inverted). Mirrors cstage cgen.c
|
||||
// N_TRYPROP nullable arm; ww's own cgtypetest carries the twin
|
||||
// (cgenexpr.ww nullable fold). (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryprop_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBP, SP\n\tPOPQ\tBP\n\tRET\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
// AX = tag. If not the success tag, this is an error; pop frame and
|
||||
// RET. Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
@@ -487,6 +503,21 @@ fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Family C (#35/#46): see the cgtryprop twin.
|
||||
cgtryunwcursor(c, n, "!");
|
||||
// Nullable `(*T | void)`: AX IS the pointer. Non-null = success
|
||||
// (leave AX as-is), null = error → abort exit(1). The pre-fix
|
||||
// path compared AX against successtag, treating null as success
|
||||
// (inverted). Mirrors cstage cgen.c N_TRYUNW nullable arm; ww's
|
||||
// own cgtypetest carries the twin. (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryunw_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
let cl: str = mklabel(c, "tryunw_ok");
|
||||
// Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
|
||||
@@ -23500,6 +23500,22 @@ fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
// cursor here (was a silent word0 unwrap); call sources keep
|
||||
// the plain cgexpr emission byte-for-byte.
|
||||
cgtryunwcursor(c, n, "?");
|
||||
// Nullable `(*T | void)`: AX IS the pointer, not a tag. Non-null
|
||||
// = success (any *T variant), null = error → propagate (RET with
|
||||
// AX=0). The pre-fix path compared AX against successtag and so
|
||||
// treated null as success (inverted). Mirrors cstage cgen.c
|
||||
// N_TRYPROP nullable arm; ww's own cgtypetest carries the twin
|
||||
// (cgenexpr.ww nullable fold). (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryprop_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBP, SP\n\tPOPQ\tBP\n\tRET\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
// AX = tag. If not the success tag, this is an error; pop frame and
|
||||
// RET. Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
@@ -23638,6 +23654,21 @@ fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Family C (#35/#46): see the cgtryprop twin.
|
||||
cgtryunwcursor(c, n, "!");
|
||||
// Nullable `(*T | void)`: AX IS the pointer. Non-null = success
|
||||
// (leave AX as-is), null = error → abort exit(1). The pre-fix
|
||||
// path compared AX against successtag, treating null as success
|
||||
// (inverted). Mirrors cstage cgen.c N_TRYUNW nullable arm; ww's
|
||||
// own cgtypetest carries the twin. (task #15/F4)
|
||||
if (isnullabletype(n.lhs)) {
|
||||
let nl: str = mklabel(c, "tryunw_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(nl);
|
||||
return;
|
||||
};
|
||||
let cl: str = mklabel(c, "tryunw_ok");
|
||||
// Success tag is dynamic (successtag / cstage cg_tagged_success_tag
|
||||
// parity, #52): 0 for success-first, the first non-error index for an
|
||||
|
||||
Reference in New Issue
Block a user