From 903c06253ea97461c9a1fee06e8217cfa434eb6a Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 13 Oct 2020 22:35:39 +0800 Subject: [PATCH] Implement operators `__static_assert` and `__static_check` --- source/compiler/sc.h | 3 ++ source/compiler/sc1.c | 100 +++++++++++++++++++++++++++++++++++++++ source/compiler/sc3.c | 10 ++++ source/compiler/sc5.c | 3 +- source/compiler/scvars.c | 4 +- 5 files changed, 117 insertions(+), 3 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index 7b151c4..caba531 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -420,6 +420,8 @@ enum { tSLEEP, tSTATE, tSTATIC, + t__STATIC_ASSERT, + t__STATIC_CHECK, tSTOCK, tSWITCH, tTAGOF, @@ -704,6 +706,7 @@ SC_FUNC void emit_parse_line(void); SC_FUNC void pragma_deprecated(symbol *sym); SC_FUNC void pragma_unused(symbol *sym,int unread,int unwritten); SC_FUNC void pragma_nodestruct(symbol *sym); +SC_FUNC cell do_static_check(int use_warning); /* function prototypes in SC2.C */ #define PUSHSTK_P(v) { stkitem s_; s_.pv=(v); pushstk(s_); } diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 10f303c..9af98c9 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -1802,6 +1802,13 @@ static void parse(void) case tFORWARD: funcstub(FALSE); break; + case t__STATIC_ASSERT: + case t__STATIC_CHECK: { + int use_warning=(tok==t__STATIC_CHECK); + do_static_check(use_warning); + needtoken(';'); + break; + } /* case */ case '}': error(54); /* unmatched closing brace */ break; @@ -8530,3 +8537,96 @@ SC_FUNC void pragma_nodestruct(symbol *sym) if (sym->ident==iVARIABLE || sym->ident==iARRAY) sym->usage |= uNODESTRUCT; } + +/* do_static_check() + * Checks compile-time assertions and triggers an error/warning. + * + * The 'use_warning' parameter is set to TRUE if warnings are to be + * used instead of errors to notify assertion failures. + */ +SC_FUNC cell do_static_check(int use_warning) +{ + int already_staging,didalloc,optmsg; + int ident,index; + int bck_litidx; + int exprstartfline,exprendfline; + cell cidx,val; + char *str; + const unsigned char *exprstart,*exprend; + + str=NULL; + didalloc=optmsg=FALSE; + index=0; + cidx=0; + + needtoken('('); + already_staging=stgget(&index,&cidx); + if (!already_staging) { + stgset(TRUE); /* start stage-buffering */ + errorset(sEXPRMARK,0); + } /* if */ + exprstart=lptr; + exprstartfline=fline; + ident=expression(&val,NULL,NULL,FALSE); + if (ident!=iCONSTEXPR) + error(8); /* must be constant expression */ + exprend=lptr; + exprendfline=fline; + stgdel(index,cidx); /* scratch generated code */ + if (!already_staging) { + errorset(sEXPRRELEASE,0); + stgset(FALSE); /* stop stage-buffering */ + } /* if */ + + /* don't bother allocating space and copying the message + * if the expression is true */ + if (val==0) { + if (exprstartfline==exprendfline) { + /* skip leading whitespaces */ + while (*exprstart==' ') + exprstart++; + /* strip the trailing ',' or ')'. as well as the whitespaces */ + exprend--; + if (*exprend==')' || *exprend==',') { + while (*(exprend-1)==' ') + exprend--; + } /* if */ + /* copy the expression string */ + str=malloc((exprend-exprstart+1)*sizeof(char)); + if (str==NULL) + error(103); /* insufficient memory */ + memcpy(str,exprstart,exprend-exprstart); + str[exprend-exprstart]='\0'; + didalloc=TRUE; + } else { + /* Currently there's no reliable way to capture multiline expressions, + * as the lexer would only keep the contents of the line the expression + * ends at, so try to print "-epression-" instead. Not the prefect + * solution, but at least it's better than not printing anything. */ + str="-expression-"; + } /* if */ + } /* if */ + + /* read the optional message */ + if (matchtoken(',')) { + if (didalloc) { + free(str); + didalloc=FALSE; + } /* if */ + optmsg=TRUE; + str=parsestringparam(val!=0,&bck_litidx); + } /* if */ + + if (val==0) { + int errnum=use_warning ? 249 /* check failed */ + : 110; /* assertion failed */ + error(errnum,(str!=NULL) ? str : ""); + if (didalloc) + free(str); + else if (optmsg && str!=NULL) + litidx=bck_litidx; /* remove the string from the literal queue */ + } /* if */ + + needtoken(')'); + return !!val; +} diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index a76c3b0..1568c4b 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1627,6 +1627,16 @@ static int hier2(value *lval) } /* if */ return FALSE; } /* case */ + case t__STATIC_ASSERT: + case t__STATIC_CHECK: { + int use_warning=(tok==t__STATIC_CHECK); + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval=do_static_check(use_warning); + lval->tag=BOOLTAG; + pc_sideeffect=TRUE; + return FALSE; + } /* case */ case t__EMIT: paranthese=matchtoken('('); emit_flags |= efEXPR; diff --git a/source/compiler/sc5.c b/source/compiler/sc5.c index 7672f45..13726cf 100644 --- a/source/compiler/sc5.c +++ b/source/compiler/sc5.c @@ -206,7 +206,8 @@ static char *warnmsg[] = { /*245*/ "enum increment \"%s %d\" has no effect on zero value (symbol \"%s\")\n", /*246*/ "multiplication overflow in enum element declaration (symbol \"%s\")\n", /*247*/ "use of operator \"~\" on a \"bool:\" value always results in \"true\"\n", -/*248*/ "possible misuse of comma operator\n" +/*248*/ "possible misuse of comma operator\n", +/*249*/ "check failed: %s\n" }; static char *noticemsg[] = { diff --git a/source/compiler/scvars.c b/source/compiler/scvars.c index 53158ad..b98fbc3 100644 --- a/source/compiler/scvars.c +++ b/source/compiler/scvars.c @@ -111,8 +111,8 @@ SC_VDEFINE char *sc_tokens[] = { "__addressof", "assert", "*begin", "break", "case", "char", "const", "continue", "default", "defined", "do", "else", "__emit", "*end", "enum", "exit", "for", "forward", "goto", "if", "__nameof", "native", "new", "operator", "__pragma", - "public", "return", "sizeof", "sleep", "state", "static", "stock", "switch", - "tagof", "*then", "while", + "public", "return", "sizeof", "sleep", "state", "static", "__static_assert", + "__static_check", "stock", "switch", "tagof", "*then", "while", "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma", "#tryinclude", "#undef", "#warning",