From 9ff38bcf80028ddb91db9604732c70a834657f5c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 5 Jul 2014 13:20:06 -0700 Subject: [PATCH 1/4] Add nulls, and more newdecl and type system features. New null_t type has a constant "null", which can be assigned to objects. Methodmaps can now be "nullable", accepting null_t types. The delete keyword now zaps non-const lvalue inputs. Fixed some newdecl arguments not accepting references. Allow functag matching to coerce arguments and return values. Allow functags to accept new-style types as a return value. --- plugins/include/handles.inc | 2 +- sourcepawn/compiler/sc.h | 5 ++ sourcepawn/compiler/sc1.c | 131 ++++++++++++++++++++++++-------- sourcepawn/compiler/sc2.c | 2 +- sourcepawn/compiler/sc3.c | 48 +++++++++++- sourcepawn/compiler/sc4.c | 6 ++ sourcepawn/compiler/sctracker.h | 1 + 7 files changed, 161 insertions(+), 34 deletions(-) diff --git a/plugins/include/handles.inc b/plugins/include/handles.inc index d811908d..924e82e5 100644 --- a/plugins/include/handles.inc +++ b/plugins/include/handles.inc @@ -78,7 +78,7 @@ native Handle:CloneHandle(Handle:hndl, Handle:plugin=INVALID_HANDLE); /** * Helper for object-oriented syntax. */ -methodmap Handle +methodmap Handle __nullable__ { public Clone() = CloneHandle; public ~Handle() = CloseHandle; diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index e81b5e9f..f9f6fce9 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -411,6 +411,8 @@ enum { tMETHODMAP, tNATIVE, tNEW, + tNULL, + tNULLABLE, tOBJECT, tOPERATOR, tPUBLIC, @@ -675,6 +677,7 @@ SC_FUNC cell array_totalsize(symbol *sym); SC_FUNC int matchtag_string(int ident, int tag); SC_FUNC int checktag_string(value *sym1, value *sym2); SC_FUNC int checktags_string(int tags[], int numtags, value *sym1); +SC_FUNC int lvalexpr(svalue *sval); /* function prototypes in SC4.C */ SC_FUNC void writeleader(symbol *root); @@ -699,6 +702,7 @@ SC_FUNC void copyarray(symbol *sym,cell size); SC_FUNC void fillarray(symbol *sym,cell size,cell value); SC_FUNC void ldconst(cell val,regid reg); SC_FUNC void moveto1(void); +SC_FUNC void move_alt(void); SC_FUNC void pushreg(regid reg); SC_FUNC void pushval(cell val); SC_FUNC void popreg(regid reg); @@ -923,6 +927,7 @@ SC_VDECL int pc_tag_string; /* global String tag */ SC_VDECL int pc_tag_void; /* global void tag */ SC_VDECL int pc_tag_object; /* root object tag */ SC_VDECL int pc_tag_bool; /* global bool tag */ +SC_VDECL int pc_tag_null_t; /* the null type */ SC_VDECL int pc_anytag; /* global any tag */ SC_VDECL int glbstringread; /* last global string read */ SC_VDECL int sc_require_newdecls; /* only newdecls are allowed */ diff --git a/sourcepawn/compiler/sc1.c b/sourcepawn/compiler/sc1.c index 4dfaf515..fd5bc9e1 100644 --- a/sourcepawn/compiler/sc1.c +++ b/sourcepawn/compiler/sc1.c @@ -79,6 +79,7 @@ int pc_tag_string = 0; int pc_tag_void = 0; int pc_tag_object = 0; int pc_tag_bool = 0; +int pc_tag_null_t = 0; static void resetglobals(void); static void initglobals(void); @@ -1349,6 +1350,7 @@ static void setconstants(void) pc_tag_void = pc_addtag_flags("void", FIXEDTAG); pc_tag_object = pc_addtag_flags("object", FIXEDTAG|OBJECTTAG); pc_tag_bool = pc_addtag("bool"); + pc_tag_null_t = pc_addtag_flags("null", FIXEDTAG|OBJECTTAG); add_constant("true",1,sGLOBAL,1); /* boolean flags */ add_constant("false",0,sGLOBAL,1); @@ -3442,7 +3444,7 @@ int parse_decl(declinfo_t *decl, int flags) // Otherwise, we have to eat a symbol to tell. if (matchsymbol(&ident)) { - if (lexpeek(tSYMBOL) || lexpeek(tOPERATOR)) { + if (lexpeek(tSYMBOL) || lexpeek(tOPERATOR) || lexpeek('&')) { // A new-style declaration only allows array dims or a symbol name, so // this is a new-style declaration. return parse_new_decl(decl, &ident.tok, flags); @@ -3973,6 +3975,9 @@ static void domethodmap(LayoutSpec spec) strcpy(map->name, mapname); if (spec == Layout_MethodMap) { map->tag = pc_addtag_flags(mapname, FIXEDTAG | METHODMAPTAG); + + if (matchtoken(tNULLABLE)) + map->nullable = TRUE; } else { constvalue *tagptr = pc_tagptr(mapname); if (!tagptr) { @@ -4032,35 +4037,48 @@ static void domethodmap(LayoutSpec spec) // delete ::= "delete" expr static void dodelete() { - int tag; - symbol *sym; + svalue sval; - int ident = doexpr(TRUE, FALSE, TRUE, FALSE, &tag, &sym, TRUE); + int lcl_staging = FALSE; + if (!staging) { + stgset(TRUE); + lcl_staging = TRUE; + assert(stgidx == 0); + } + int lcl_stgidx = stgidx; + + int ident = lvalexpr(&sval); needtoken(tTERM); switch (ident) { case iFUNCTN: case iREFFUNC: error(115, "functions"); - return; + goto cleanup; case iARRAY: case iREFARRAY: case iARRAYCELL: case iARRAYCHAR: - error(115, "arrays"); - return; + { + symbol *sym = sval.val.sym; + if (!sym || sym->dim.array.level > 0) { + error(115, "arrays"); + goto cleanup; + } + break; + } } - if (tag == 0) { + if (sval.val.tag == 0) { error(115, "primitive types or enums"); - return; + goto cleanup; } - methodmap_t *map = methodmap_find_by_tag(tag); + methodmap_t *map = methodmap_find_by_tag(sval.val.tag); if (!map) { - error(115, pc_tagname(tag)); - return; + error(115, pc_tagname(sval.val.tag)); + goto cleanup; } { @@ -4076,24 +4094,53 @@ static void dodelete() if (!map || !map->dtor) { error(115, layout_spec_name(map->spec), map->name); - return; + goto cleanup; + } + + // Only zap non-const lvalues. + int zap = sval.lvalue; + if (zap && sval.val.sym && (sval.val.sym->usage & uCONST)) + zap = FALSE; + + int popaddr = FALSE; + if (sval.lvalue) { + if (zap) { + if (sval.val.ident == iARRAYCELL || sval.val.ident == iARRAYCHAR) { + // Address is in pri so we have to save it. + pushreg(sPRI); + popaddr = TRUE; + } + } + rvalue(&sval.val); } - // For some reason, we don't get a sysreq.n once this passes through the - // peephole optimizer. I can't tell why. -dvander - // // push.pri // push.c 1 // sysreq.c N 1 // stack 8 pushreg(sPRI); - markexpr(sPARM,NULL,0); { pushval(1); ffcall(map->dtor->target, NULL, 1); markusage(map->dtor->target, uREAD); } - markexpr(sEXPR,NULL,0); + + if (zap) { + if (popaddr) + popreg(sALT); + + // Store 0 back. + ldconst(0, sPRI); + store(&sval.val); + } + +cleanup: + if (lcl_staging) { + stgout(lcl_stgidx); + stgset(FALSE); + } + + markexpr(sEXPR, NULL, 0); } /** @@ -4113,20 +4160,44 @@ static void dofuncenum(int listmode) /* get the explicit tag (required!) */ int l = lex(&val,&str); - if (l != tSYMBOL) - { - if (listmode == FALSE && l == tPUBLIC) - { - isNewStyle = 1; - newStyleTag = pc_addtag(NULL); - l = lex(&val, &str); - if (l != tSYMBOL) - { + if (l != tSYMBOL) { + if (listmode == FALSE && l == tPUBLIC) { + isNewStyle = TRUE; + switch (lex(&val, &str)) { + case tOBJECT: + newStyleTag = pc_tag_object; + break; + case tINT: + newStyleTag = 0; + break; + case tVOID: + newStyleTag = pc_tag_void; + break; + case tCHAR: + newStyleTag = pc_tag_string; + break; + case tLABEL: + newStyleTag = pc_addtag(str); + break; + case tSYMBOL: + // Check whether this is new-style declaration. + // we'll port this all to parse_decl() sometime. + if (lexpeek('(')) + lexpush(); + else + newStyleTag = pc_addtag(str); + break; + default: error(93); } - } - else - { + + if (!needtoken(tSYMBOL)) { + lexclr(TRUE); + litidx = 0; + return; + } + l = tokeninfo(&val, &str); + } else { error(93); } } diff --git a/sourcepawn/compiler/sc2.c b/sourcepawn/compiler/sc2.c index d3b42718..4ba0982c 100644 --- a/sourcepawn/compiler/sc2.c +++ b/sourcepawn/compiler/sc2.c @@ -1956,7 +1956,7 @@ char *sc_tokens[] = { "goto", "if", "int", "methodmap", - "native", "new", + "native", "new", "null", "__nullable__", "object", "operator", "public", "return", diff --git a/sourcepawn/compiler/sc3.c b/sourcepawn/compiler/sc3.c index 8472122c..9c6a7d2a 100644 --- a/sourcepawn/compiler/sc3.c +++ b/sourcepawn/compiler/sc3.c @@ -356,6 +356,23 @@ static int matchobjecttags(int formaltag, int actualtag, int flags) // objects never coerce to non-objects, YET. if ((formaltag & OBJECTTAG) && !(actualtag & OBJECTTAG)) return obj_typeerror(132, formaltag, actualtag); + + if (actualtag == pc_tag_null_t) { + // All objects are nullable. + if (formaltag & OBJECTTAG) + return TRUE; + + // Some methodmaps are nullable. + methodmap_t *map = methodmap_find_by_tag(formaltag); + for (; map; map = map->parent) { + if (map->nullable) + return TRUE; + } + + error(148, pc_tagname(formaltag)); + return FALSE; + } + if (!(formaltag & OBJECTTAG) && (actualtag & OBJECTTAG)) return obj_typeerror(131, formaltag, actualtag); @@ -375,6 +392,15 @@ static int matchobjecttags(int formaltag, int actualtag, int flags) return obj_typeerror(133, formaltag, actualtag); } +static int matchreturntag(functag_t *t, symbol *sym) +{ + if (t->ret_tag == sym->tag) + return TRUE; + if (t->ret_tag == pc_tag_void && (sym->tag == 0 && !(sym->usage & uRETVALUE))) + return TRUE; + return FALSE; +} + static int matchfunctags(int formaltag, int actualtag) { if (actualtag == pc_functag || (formaltag == pc_functag && actualtag & FUNCTAG)) @@ -447,7 +473,7 @@ static int matchfunctags(int formaltag, int actualtag) arginfo *func_arg; funcarg_t *enum_arg; /* Check return type first. */ - if (t->ret_tag != sym->tag) { + if (!matchreturntag(t, sym)) { t = t->next; continue; } @@ -496,7 +522,7 @@ static int matchfunctags(int formaltag, int actualtag) } /* They should all be in the same order just for clarity... */ for (i=0; itagcount; i++) { - if (enum_arg->tags[i] != func_arg->tags[i]) { + if (!matchtag(func_arg->tags[i], enum_arg->tags[i], MATCHTAG_SILENT|MATCHTAG_COERCE)) { skip = 1; break; } @@ -954,6 +980,19 @@ static cell calc(cell left,void (*oper)(),cell right,char *boolresult) return 0; } +SC_FUNC int lvalexpr(svalue *sval) +{ + memset(sval, 0, sizeof(*sval)); + + errorset(sEXPRMARK, 0); + pushheaplist(); + sval->lvalue = hier14(&sval->val); + popheaplist(); + errorset(sEXPRRELEASE, 0); + + return sval->val.ident; +} + SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult,value *_lval) { value lval={0}; @@ -2949,6 +2988,11 @@ static int constant(value *lval) lval->tag=sym->tag; lval->sym=sym; markusage(sym,uREAD); + } else if (tok==tNULL) { + lval->constval = 0; + ldconst(lval->constval, sPRI); + lval->ident = iCONSTEXPR; + lval->tag = pc_tag_null_t; } else if (tok==tNUMBER) { lval->constval=val; ldconst(lval->constval,sPRI); diff --git a/sourcepawn/compiler/sc4.c b/sourcepawn/compiler/sc4.c index 7ed99a1c..de82317a 100644 --- a/sourcepawn/compiler/sc4.c +++ b/sourcepawn/compiler/sc4.c @@ -637,6 +637,12 @@ SC_FUNC void moveto1(void) code_idx+=opcodes(1)+opargs(0); } +SC_FUNC void move_alt(void) +{ + stgwrite("\tmove.alt\n"); + code_idx+=opcodes(1)+opargs(0); +} + /* Push primary or the alternate register onto the stack */ SC_FUNC void pushreg(regid reg) diff --git a/sourcepawn/compiler/sctracker.h b/sourcepawn/compiler/sctracker.h index 72901aec..372073c4 100644 --- a/sourcepawn/compiler/sctracker.h +++ b/sourcepawn/compiler/sctracker.h @@ -92,6 +92,7 @@ typedef struct methodmap_s struct methodmap_s *next; struct methodmap_s *parent; int tag; + int nullable; LayoutSpec spec; char name[sNAMEMAX+1]; methodmap_method_t **methods; From a7342f3fa2e3fb38426255a7bb7b9143a69a104e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 5 Jul 2014 13:28:55 -0700 Subject: [PATCH 2/4] Fix AMBuildScript compatibility issue with older objdirs. --- AMBuildScript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AMBuildScript b/AMBuildScript index 83ad62ca..b4541d3c 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -73,7 +73,9 @@ class SMConfig(object): self.versionlib = None def use_auto_versioning(self): - return builder.backend == 'amb2' and not builder.options.disable_auto_versioning + if builder.backend != 'amb2': + return False + return not getattr(builder.options, 'disable_auto_versioning', False) @property def tag(self): From 9ae3256bc05b24fa2f299394df992541e0d5e6aa Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 5 Jul 2014 13:31:10 -0700 Subject: [PATCH 3/4] Rename the null tag to null_t. --- sourcepawn/compiler/sc1.c | 2 +- sourcepawn/compiler/sc5.scp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sourcepawn/compiler/sc1.c b/sourcepawn/compiler/sc1.c index fd5bc9e1..60054bef 100644 --- a/sourcepawn/compiler/sc1.c +++ b/sourcepawn/compiler/sc1.c @@ -1350,7 +1350,7 @@ static void setconstants(void) pc_tag_void = pc_addtag_flags("void", FIXEDTAG); pc_tag_object = pc_addtag_flags("object", FIXEDTAG|OBJECTTAG); pc_tag_bool = pc_addtag("bool"); - pc_tag_null_t = pc_addtag_flags("null", FIXEDTAG|OBJECTTAG); + pc_tag_null_t = pc_addtag_flags("null_t", FIXEDTAG|OBJECTTAG); add_constant("true",1,sGLOBAL,1); /* boolean flags */ add_constant("false",0,sGLOBAL,1); diff --git a/sourcepawn/compiler/sc5.scp b/sourcepawn/compiler/sc5.scp index c2341156..8ac1517c 100644 --- a/sourcepawn/compiler/sc5.scp +++ b/sourcepawn/compiler/sc5.scp @@ -191,6 +191,7 @@ static char *errmsg[] = { /*145*/ "invalid type expression\n", /*146*/ "#pragma newdecls must be required or optional\n", /*147*/ "new-style declarations are required\n", +/*148*/ "cannot assign null to a non-nullable type\n", #else "\247\255\311\232\273k\214:\234\306bu\201fo\223\204\222\012", "\202l\224\251s\206g\344\352e\233\201(\242\247\323\267\202) \254 f\252low ea\277 \042c\343e\042\012", From 1277bbd50e4ee05723483a0f499fa6327abf560f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 5 Jul 2014 14:15:44 -0700 Subject: [PATCH 4/4] Mark the expression before flushing the staging buffer. --- sourcepawn/compiler/sc1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sourcepawn/compiler/sc1.c b/sourcepawn/compiler/sc1.c index 60054bef..f2931806 100644 --- a/sourcepawn/compiler/sc1.c +++ b/sourcepawn/compiler/sc1.c @@ -4134,13 +4134,13 @@ static void dodelete() store(&sval.val); } + markexpr(sEXPR, NULL, 0); + cleanup: if (lcl_staging) { stgout(lcl_stgidx); stgset(FALSE); } - - markexpr(sEXPR, NULL, 0); } /**