From a6c3ce6ec03516d850e6b987fcbe95aba192b76b Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Wed, 18 Feb 2015 04:22:25 +0000 Subject: [PATCH] The "open a whisky and cut your finger open" patch Make integer constant parsing C99 compliant --- tccpp.c | 75 +++++++++++++++------------- tests/tests2/72_long_long_constant.c | 17 +++++++ tests/tests2/Makefile | 7 +-- 3 files changed, 62 insertions(+), 37 deletions(-) create mode 100644 tests/tests2/72_long_long_constant.c diff --git a/tccpp.c b/tccpp.c index bc2d4a2..42f34aa 100644 --- a/tccpp.c +++ b/tccpp.c @@ -2012,7 +2012,8 @@ static void parse_number(const char *p) } } else { unsigned long long n, n1; - int lcount, ucount; + int lcount, ucount, must_64bit; + const char *p1; /* integer number */ *q = '\0'; @@ -2025,17 +2026,16 @@ static void parse_number(const char *p) while(1) { t = *q++; /* no need for checks except for base 10 / 8 errors */ - if (t == '\0') { + if (t == '\0') break; - } else if (t >= 'a') { + else if (t >= 'a') t = t - 'a' + 10; - } else if (t >= 'A') { + else if (t >= 'A') t = t - 'A' + 10; - } else { + else t = t - '0'; - if (t >= b) - tcc_error("invalid digit"); - } + if (t >= b) + tcc_error("invalid digit"); n1 = n; n = n * b + t; /* detect overflow */ @@ -2043,50 +2043,57 @@ static void parse_number(const char *p) if (n < n1) tcc_error("integer constant overflow"); } - - /* XXX: not exactly ANSI compliant */ - if ((n & 0xffffffff00000000LL) != 0) { - if ((n >> 63) != 0) - tok = TOK_CULLONG; - else - tok = TOK_CLLONG; - } else if (n > 0x7fffffff) { - tok = TOK_CUINT; - } else { - tok = TOK_CINT; - } - lcount = 0; - ucount = 0; + + /* Determine the characteristics (unsigned and/or 64bit) the type of + the constant must have according to the constant suffix(es) */ + lcount = ucount = must_64bit = 0; + p1 = p; for(;;) { t = toup(ch); if (t == 'L') { if (lcount >= 2) tcc_error("three 'l's in integer constant"); + if (lcount && *(p - 1) != ch) + tcc_error("incorrect integer suffix: %s", p1); lcount++; #if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE - if (lcount == 2) { -#endif - if (tok == TOK_CINT) - tok = TOK_CLLONG; - else if (tok == TOK_CUINT) - tok = TOK_CULLONG; -#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE - } + if (lcount == 2) #endif + must_64bit = 1; ch = *p++; } else if (t == 'U') { if (ucount >= 1) tcc_error("two 'u's in integer constant"); ucount++; - if (tok == TOK_CINT) - tok = TOK_CUINT; - else if (tok == TOK_CLLONG) - tok = TOK_CULLONG; ch = *p++; } else { break; } } + + /* Whether 64 bits are needed to hold the constant's value */ + if (n & 0xffffffff00000000LL || must_64bit) { + tok = TOK_CLLONG; + n1 = n >> 32; + } else { + tok = TOK_CINT; + n1 = n; + } + + /* Whether type must be unsigned to hold the constant's value */ + if (ucount || ((n1 >> 31) && (b != 10))) { + if (tok == TOK_CLLONG) + tok = TOK_CULLONG; + else + tok = TOK_CUINT; + /* If decimal and no unsigned suffix, bump to 64 bits or throw error */ + } else if (n1 >> 31) { + if (tok == TOK_CINT) + tok = TOK_CLLONG; + else + tcc_error("integer constant overflow"); + } + if (tok == TOK_CINT || tok == TOK_CUINT) tokc.ui = n; else diff --git a/tests/tests2/72_long_long_constant.c b/tests/tests2/72_long_long_constant.c new file mode 100644 index 0000000..a698fd6 --- /dev/null +++ b/tests/tests2/72_long_long_constant.c @@ -0,0 +1,17 @@ +#include + +int main() +{ + long long int res = 0; + + if (res < -2147483648LL) { + printf("Error: 0 < -2147483648\n"); + return 1; + } + else + if (2147483647LL < res) { + printf("Error: 2147483647 < 0\n"); + return 2; + } + return 0; +} diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index ec636ab..8d8a5e7 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -91,6 +91,7 @@ TESTS = \ 69_macro_param_list_err_2.test \ 70_floating_point_literals.test \ 71_macro_empty_arg.test \ + 72_long_long_constant.test \ # 34_array_assignment.test -- array assignment is not in C standard @@ -113,14 +114,14 @@ ARGS = all test: $(filter-out $(SKIP),$(TESTS)) -%.test: %.c %.expect +%.test: %.c @echo Test: $*... @$(TCC) -run $< $(ARGS) 2>&1 | grep -v 'warning: soft float ABI currently not supported: default to softfp' >$*.output || true - @diff -bu $*.expect $*.output && rm -f $*.output + @diff -Nbu $*.expect $*.output && rm -f $*.output @($(TCC) $< -o $*.exe && ./$*.exe $(ARGS)) 2>&1 | grep -v 'warning: soft float ABI currently not supported: default to softfp' >$*.output2 || true - @diff -bu $*.expect $*.output2 && rm -f $*.output2 $*.exe + @diff -Nbu $*.expect $*.output2 && rm -f $*.output2 $*.exe clean: rm -vf fred.txt *.output* *.exe