diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index 0c5c8471..3b81465c 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -672,8 +672,9 @@ SC_FUNC symbol *addvariable3(declinfo_t *decl,cell addr,int vclass,int slength); SC_FUNC int getlabel(void); SC_FUNC char *itoh(ucell val); -#define MATCHTAG_COERCE 0x1 // allow coercion -#define MATCHTAG_SILENT 0x2 // silence the error(213) warning +#define MATCHTAG_COERCE 0x1 // allow coercion +#define MATCHTAG_SILENT 0x2 // silence the error(213) warning +#define MATCHTAG_COMMUTATIVE 0x4 // order does not matter /* function prototypes in SC3.C */ SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, diff --git a/sourcepawn/compiler/sc1.c b/sourcepawn/compiler/sc1.c index 818ca6c4..b0c99bef 100644 --- a/sourcepawn/compiler/sc1.c +++ b/sourcepawn/compiler/sc1.c @@ -4056,7 +4056,7 @@ static void domethodmap(LayoutSpec spec) if (spec == Layout_MethodMap) { map->tag = pc_addtag_flags(mapname, FIXEDTAG | METHODMAPTAG); - if (matchtoken(tNULLABLE)) + if (matchtoken(tNULLABLE) || (parent && parent->nullable)) map->nullable = TRUE; } else { constvalue *tagptr = pc_tagptr(mapname); diff --git a/sourcepawn/compiler/sc3.c b/sourcepawn/compiler/sc3.c index bf2d630c..4e6e31f9 100644 --- a/sourcepawn/compiler/sc3.c +++ b/sourcepawn/compiler/sc3.c @@ -353,6 +353,15 @@ static int obj_typeerror(int id, int tag1, int tag2) static int matchobjecttags(int formaltag, int actualtag, int flags) { + if ((flags & MATCHTAG_COMMUTATIVE) && + (formaltag == pc_tag_null_t || formaltag == pc_tag_nullfunc_t)) + { + // Bypass the check immediately after for non-object coercion. + int tmp = actualtag; + actualtag = formaltag; + formaltag = tmp; + } + // objects never coerce to non-objects, YET. if ((formaltag & OBJECTTAG) && !(actualtag & OBJECTTAG)) return obj_typeerror(132, formaltag, actualtag); @@ -372,12 +381,11 @@ static int matchobjecttags(int formaltag, int actualtag, int flags) if (formaltag & OBJECTTAG) return TRUE; - // Some methodmaps are nullable. + // Some methodmaps are nullable. The nullable property is inherited + // automatically. methodmap_t *map = methodmap_find_by_tag(formaltag); - for (; map; map = map->parent) { - if (map->nullable) - return TRUE; - } + if (map->nullable) + return TRUE; error(148, pc_tagname(formaltag)); return FALSE; @@ -923,8 +931,9 @@ static void plnge2(void (*oper)(void), matchtag(lval1->tag,lval2->tag,FALSE); lval1->constval=calc(lval1->constval,oper,lval2->constval,&lval1->boolresult); } else { + // For the purposes of tag matching, we consider the order to be irrelevant. if (!checktag_string(lval1, lval2)) - matchtag(lval1->tag,lval2->tag,FALSE); + matchtag(lval1->tag, lval2->tag, MATCHTAG_COMMUTATIVE); (*oper)(); /* do the (signed) operation */ lval1->ident=iEXPRESSION; } /* if */ diff --git a/sourcepawn/compiler/tests/ok-null-compare.sp b/sourcepawn/compiler/tests/ok-null-compare.sp new file mode 100644 index 00000000..044c0bfd --- /dev/null +++ b/sourcepawn/compiler/tests/ok-null-compare.sp @@ -0,0 +1,19 @@ +enum Handle: +{ +} + +methodmap Handle __nullable__ +{ +} + +methodmap StringMap < Handle +{ +} + +native Log(const char[] fmt, any:...) + +public main() +{ + StringMap f + Log("hello %d", f != null) +}