fix cases where compoundlits are constant/get an entity
[cparser] / preprocessor.c
index 9c457a3..74b8277 100644 (file)
@@ -82,6 +82,7 @@ struct pp_input_t {
 struct searchpath_entry_t {
        const char         *path;
        searchpath_entry_t *next;
+       bool                is_system_path;
 };
 
 static pp_input_t      input;
@@ -110,7 +111,16 @@ static unsigned          argument_brace_count;
 static strset_t          stringset;
 static token_kind_t      last_token;
 
-static searchpath_entry_t *searchpath;
+struct searchpath_t {
+       searchpath_entry_t  *first;
+       searchpath_entry_t **anchor;
+       bool                 is_system_path;
+};
+
+searchpath_t bracket_searchpath = { NULL, &bracket_searchpath.first, false };
+searchpath_t quote_searchpath   = { NULL, &quote_searchpath.first,   false };
+searchpath_t system_searchpath  = { NULL, &system_searchpath.first,  true  };
+searchpath_t after_searchpath   = { NULL, &after_searchpath.first,   true  };
 
 static whitespace_info_t next_info; /* valid if had_whitespace is true */
 static whitespace_info_t info;
@@ -126,6 +136,11 @@ static symbol_t *symbol_percentcolon;
 static symbol_t *symbol_percentcolonpercentcolon;
 static symbol_t *symbol_percentgreater;
 
+static symbol_t *symbol_L;
+static symbol_t *symbol_U;
+static symbol_t *symbol_u;
+static symbol_t *symbol_u8;
+
 static void init_symbols(void)
 {
        symbol_colongreater             = symbol_table_insert(":>");
@@ -134,18 +149,24 @@ static void init_symbols(void)
        symbol_percentcolon             = symbol_table_insert("%:");
        symbol_percentcolonpercentcolon = symbol_table_insert("%:%:");
        symbol_percentgreater           = symbol_table_insert("%>");
+
+       symbol_L  = symbol_table_insert("L");
+       symbol_U  = symbol_table_insert("U");
+       symbol_u  = symbol_table_insert("u");
+       symbol_u8 = symbol_table_insert("u8");
 }
 
-void switch_pp_input(FILE *const file, char const *const filename, searchpath_entry_t *const path)
+void switch_pp_input(FILE *const file, char const *const filename, searchpath_entry_t *const path, bool const is_system_header)
 {
-       input.file                = file;
-       input.input               = input_from_stream(file, NULL);
-       input.bufend              = NULL;
-       input.bufpos              = NULL;
-       input.output_line         = 0;
-       input.position.input_name = filename;
-       input.position.lineno     = 1;
-       input.path                = path;
+       input.file                      = file;
+       input.input                     = input_from_stream(file, NULL);
+       input.bufend                    = NULL;
+       input.bufpos                    = NULL;
+       input.output_line               = 0;
+       input.position.input_name       = filename;
+       input.position.lineno           = 1;
+       input.position.is_system_header = is_system_header;
+       input.path                      = path;
 
        /* indicate that we're at a new input */
        print_line_directive(&input.position, input_stack != NULL ? "1" : NULL);
@@ -448,7 +469,82 @@ static utf32 parse_universal_char(unsigned const n_digits)
        return v;
 }
 
-static bool is_universal_char_valid_identifier(utf32 const v)
+static bool is_universal_char_valid_identifier_c99(utf32 const v)
+{
+       static const utf32 single_chars[] = {
+               0x00AA, 0x00BA, 0x0386, 0x038C, 0x03DA, 0x03DC, 0x03DE, 0x03E0,
+               0x1F59, 0x1F5B, 0x1F5D, 0x05BF, 0x09B2, 0x0A02, 0x0A5E, 0x0A74,
+               0x0A8D, 0x0AD0, 0x0AE0, 0x0B9C, 0x0CDE, 0x0E84, 0x0E8A, 0x0E8D,
+               0x0EA5, 0x0EA7, 0x0EC6, 0x0F00, 0x0F35, 0x0F37, 0x0F39, 0x0F97,
+               0x0FB9, 0x00B5, 0x00B7, 0x02BB, 0x037A, 0x0559, 0x093D, 0x0B3D,
+               0x1FBE, 0x2102, 0x2107, 0x2115, 0x2124, 0x2126, 0x2128
+       };
+
+       static const utf32 ranges[][2] = {
+               {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x01F5}, {0x01FA, 0x0217},
+               {0x0250, 0x02A8}, {0x1E00, 0x1E9B}, {0x1EA0, 0x1EF9}, {0x0388, 0x038A},
+               {0x038E, 0x03A1}, {0x03A3, 0x03CE}, {0x03D0, 0x03D6}, {0x03E2, 0x03F3},
+               {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
+               {0x1F50, 0x1F57}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FBC},
+               {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB},
+               {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x0401, 0x040C},
+               {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, {0x0490, 0x04C4},
+               {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, {0x04EE, 0x04F5},
+               {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0561, 0x0587}, {0x05B0, 0x05B9},
+               {0x05BB, 0x05BD}, {0x05C1, 0x05C2}, {0x05D0, 0x05EA}, {0x05F0, 0x05F2},
+               {0x0621, 0x063A}, {0x0640, 0x0652}, {0x0670, 0x06B7}, {0x06BA, 0x06BE},
+               {0x06C0, 0x06CE}, {0x06D0, 0x06DC}, {0x06E5, 0x06E8}, {0x06EA, 0x06ED},
+               {0x0901, 0x0903}, {0x0905, 0x0939}, {0x093E, 0x094D}, {0x0950, 0x0952},
+               {0x0958, 0x0963}, {0x0981, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990},
+               {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B6, 0x09B9}, {0x09BE, 0x09C4},
+               {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3},
+               {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28},
+               {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39},
+               {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A59, 0x0A5C},
+               {0x0A81, 0x0A83}, {0x0A85, 0x0A8B}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8},
+               {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0AC5},
+               {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C},
+               {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33},
+               {0x0B36, 0x0B39}, {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D},
+               {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A},
+               {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9E, 0x0B9F},
+               {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, {0x0BB7, 0x0BB9},
+               {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0C01, 0x0C03},
+               {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C33},
+               {0x0C35, 0x0C39}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D},
+               {0x0C60, 0x0C61}, {0x0C82, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90},
+               {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBE, 0x0CC4},
+               {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CE0, 0x0CE1}, {0x0D02, 0x0D03},
+               {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D28}, {0x0D2A, 0x0D39},
+               {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D60, 0x0D61},
+               {0x0E01, 0x0E3A}, {0x0E40, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E87, 0x0E88},
+               {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EAA, 0x0EAB},
+               {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB9}, {0x0EBB, 0x0EBD}, {0x0EC0, 0x0EC4},
+               {0x0EC8, 0x0ECD}, {0x0EDC, 0x0EDD}, {0x0F18, 0x0F19}, {0x0F3E, 0x0F47},
+               {0x0F49, 0x0F69}, {0x0F71, 0x0F84}, {0x0F86, 0x0F8B}, {0x0F90, 0x0F95},
+               {0x0F99, 0x0FAD}, {0x0FB1, 0x0FB7}, {0x10A0, 0x10C5}, {0x10D0, 0x10F6},
+               {0x3041, 0x3093}, {0x309B, 0x309C}, {0x30A1, 0x30F6}, {0x30FB, 0x30FC},
+               {0x3105, 0x312C}, {0x4E00, 0x9FA5}, {0xAC00, 0xD7A3}, {0x0660, 0x0669},
+               {0x06F0, 0x06F9}, {0x0966, 0x096F}, {0x09E6, 0x09EF}, {0x0A66, 0x0A6F},
+               {0x0AE6, 0x0AEF}, {0x0B66, 0x0B6F}, {0x0BE7, 0x0BEF}, {0x0C66, 0x0C6F},
+               {0x0CE6, 0x0CEF}, {0x0D66, 0x0D6F}, {0x0E50, 0x0E59}, {0x0ED0, 0x0ED9},
+               {0x0F20, 0x0F33}, {0x02B0, 0x02B8}, {0x02BD, 0x02C1}, {0x02D0, 0x02D1},
+               {0x02E0, 0x02E4}, {0x203F, 0x2040}, {0x210A, 0x2113}, {0x2118, 0x211D},
+               {0x212A, 0x2131}, {0x2133, 0x2138}, {0x2160, 0x2182}, {0x3005, 0x3007},
+               {0x3021, 0x3029},
+       };
+       for (size_t i = 0; i < sizeof(ranges)/sizeof(ranges[0]); ++i) {
+               if (ranges[i][0] <= v && v <= ranges[i][1])
+                       return true;
+       }
+       for (size_t i = 0; i < sizeof(single_chars)/sizeof(single_chars[0]); ++i) {
+               if (v == single_chars[i])
+                       return true;
+       }
+       return false;
+}
+
+static bool is_universal_char_valid_identifier_c11(utf32 const v)
 {
        /* C11 Annex D.1 */
        if (                v == 0x000A8) return true;
@@ -499,14 +595,24 @@ static bool is_universal_char_valid_identifier(utf32 const v)
        return false;
 }
 
-static bool is_universal_char_valid_identifier_start(utf32 const v)
+static bool is_universal_char_valid_identifier(utf32 const v)
+{
+       if (c_mode & _C11)
+               return is_universal_char_valid_identifier_c11(v);
+       return is_universal_char_valid_identifier_c99(v);
+}
+
+static bool is_universal_char_invalid_identifier_start(utf32 const v)
 {
+       if (! (c_mode & _C11))
+               return false;
+
        /* C11 Annex D.2 */
-       if (0x0300 <= v && v <= 0x036F) return false;
-       if (0x1DC0 <= v && v <= 0x1DFF) return false;
-       if (0x20D0 <= v && v <= 0x20FF) return false;
-       if (0xFE20 <= v && v <= 0xFE2F) return false;
-       return true;
+       if (0x0300 <= v && v <= 0x036F) return true;
+       if (0x1DC0 <= v && v <= 0x1DFF) return true;
+       if (0x20D0 <= v && v <= 0x20FF) return true;
+       if (0xFE20 <= v && v <= 0xFE2F) return true;
+       return false;
 }
 
 /**
@@ -588,6 +694,18 @@ string_t make_string(char const *const string)
        return sym_make_string(STRING_ENCODING_CHAR);
 }
 
+static utf32 get_string_encoding_limit(string_encoding_t const enc)
+{
+       switch (enc) {
+       case STRING_ENCODING_CHAR:   return 0xFF;
+       case STRING_ENCODING_CHAR16: return 0xFFFF;
+       case STRING_ENCODING_CHAR32: return 0xFFFFFFFF;
+       case STRING_ENCODING_UTF8:   return 0xFFFFFFFF;
+       case STRING_ENCODING_WIDE:   return 0xFFFFFFFF; // FIXME depends on settings
+       }
+       panic("invalid string encoding");
+}
+
 static void parse_string(utf32 const delimiter, token_kind_t const kind,
                          string_encoding_t const enc,
                          char const *const context)
@@ -596,15 +714,16 @@ static void parse_string(utf32 const delimiter, token_kind_t const kind,
 
        eat(delimiter);
 
+       utf32 const limit = get_string_encoding_limit(enc);
        while (true) {
                switch (input.c) {
                case '\\': {
                        if (resolve_escape_sequences) {
                                utf32 const tc = parse_escape_sequence();
+                               if (tc > limit) {
+                                       warningf(WARN_OTHER, &pp_token.base.source_position, "escape sequence out of range");
+                               }
                                if (enc == STRING_ENCODING_CHAR) {
-                                       if (tc >= 0x100) {
-                                               warningf(WARN_OTHER, &pp_token.base.source_position, "escape sequence out of range");
-                                       }
                                        obstack_1grow(&symbol_obstack, tc);
                                } else {
                                        obstack_grow_utf8(&symbol_obstack, tc);
@@ -1037,6 +1156,17 @@ static inline void eat_token(token_kind_t const kind)
        next_input_token();
 }
 
+static string_encoding_t identify_encoding_prefix(symbol_t *const sym)
+{
+       if (sym == symbol_L) return STRING_ENCODING_WIDE;
+       if (c_mode & _C11) {
+               if (sym == symbol_U)  return STRING_ENCODING_CHAR32;
+               if (sym == symbol_u)  return STRING_ENCODING_CHAR16;
+               if (sym == symbol_u8) return STRING_ENCODING_UTF8;
+       }
+       return STRING_ENCODING_CHAR;
+}
+
 static void parse_symbol(void)
 {
        assert(obstack_object_size(&symbol_obstack) == 0);
@@ -1068,7 +1198,7 @@ universal:
                                                           "universal character \\%c%0*X is not valid in an identifier",
                                                           n == 4 ? 'u' : 'U', (int)n, v);
                                        }
-                               } else if (obstack_object_size(&symbol_obstack) == 0 && !is_universal_char_valid_identifier_start(v)) {
+                               } else if (obstack_object_size(&symbol_obstack) == 0 && is_universal_char_invalid_identifier_start(v)) {
                                        errorf(&input.position,
                                                   "universal character \\%c%0*X is not valid as start of an identifier",
                                                   n == 4 ? 'u' : 'U', (int)n, v);
@@ -1094,19 +1224,26 @@ end_symbol:
        obstack_1grow(&symbol_obstack, '\0');
        char *string = obstack_finish(&symbol_obstack);
 
-       /* might be a wide string or character constant ( L"string"/L'c' ) */
-       if (input.c == '"' && string[0] == 'L' && string[1] == '\0') {
-               obstack_free(&symbol_obstack, string);
-               parse_string_literal(STRING_ENCODING_WIDE);
-               return;
-       } else if (input.c == '\'' && string[0] == 'L' && string[1] == '\0') {
-               obstack_free(&symbol_obstack, string);
-               parse_character_constant(STRING_ENCODING_WIDE);
-               return;
-       }
-
        symbol_t *symbol = symbol_table_insert(string);
 
+       /* Might be a prefixed string or character constant: L/U/u/u8"string". */
+       if (input.c == '"') {
+               string_encoding_t const enc = identify_encoding_prefix(symbol);
+               if (enc != STRING_ENCODING_CHAR) {
+                       parse_string_literal(enc);
+                       return;
+               }
+       } else if (input.c == '\'') {
+               string_encoding_t const enc = identify_encoding_prefix(symbol);
+               if (enc != STRING_ENCODING_CHAR) {
+                       if (enc == STRING_ENCODING_UTF8) {
+                               errorf(&pp_token.base.source_position, "'u8' is not a valid encoding for a chracter constant");
+                       }
+                       parse_character_constant(enc);
+                       return;
+               }
+       }
+
        pp_token.kind        = symbol->ID;
        pp_token.base.symbol = symbol;
 
@@ -1395,8 +1532,7 @@ digraph_percentcolon:
        default:
 dollar_sign:
                if (error_on_unknown_chars) {
-                       errorf(&pp_token.base.source_position,
-                              "unknown character '%lc' found\n", input.c);
+                       errorf(&pp_token.base.source_position, "unknown character '%lc' found", input.c);
                        next_char();
                        goto restart;
                } else {
@@ -1940,14 +2076,16 @@ finish_headername:
        return identified;
 }
 
-static bool do_include(bool const system_include, bool const include_next, char const *const headername)
+static bool do_include(bool const bracket_include, bool const include_next, char const *const headername)
 {
        size_t const        headername_len = strlen(headername);
        searchpath_entry_t *entry;
        if (include_next) {
-               entry = input.path ? input.path->next : searchpath;
+               entry = input.path      ? input.path->next
+                     : bracket_include ? bracket_searchpath.first
+                     : quote_searchpath.first;
        } else {
-               if (!system_include) {
+               if (!bracket_include) {
                        /* put dirname of current input on obstack */
                        const char *filename   = input.position.input_name;
                        const char *last_slash = strrchr(filename, '/');
@@ -1964,12 +2102,13 @@ static bool do_include(bool const system_include, bool const include_next, char
 
                        FILE *file = fopen(full_name, "r");
                        if (file != NULL) {
-                               switch_pp_input(file, full_name, NULL);
+                               switch_pp_input(file, full_name, NULL, false);
                                return true;
                        }
+                       entry = quote_searchpath.first;
+               } else {
+                       entry = bracket_searchpath.first;
                }
-
-               entry = searchpath;
        }
 
        assert(obstack_object_size(&symbol_obstack) == 0);
@@ -1986,7 +2125,7 @@ static bool do_include(bool const system_include, bool const include_next, char
                FILE *file          = fopen(complete_path, "r");
                if (file != NULL) {
                        const char *filename = identify_string(complete_path);
-                       switch_pp_input(file, filename, entry);
+                       switch_pp_input(file, filename, entry, entry->is_system_path);
                        return true;
                } else {
                        obstack_free(&symbol_obstack, complete_path);
@@ -2503,24 +2642,21 @@ restart:
        }
 }
 
-
-static void prepend_include_path(const char *path)
+void append_include_path(searchpath_t *paths, const char *path)
 {
        searchpath_entry_t *entry = OALLOCZ(&config_obstack, searchpath_entry_t);
-       entry->path = path;
-       entry->next = searchpath;
-       searchpath  = entry;
+       entry->path           = path;
+       entry->is_system_path = paths->is_system_path;
+
+       *paths->anchor = entry;
+       paths->anchor  = &entry->next;
 }
 
-static void setup_include_path(void)
+static void append_env_paths(searchpath_t *paths, const char *envvar)
 {
-       /* built-in paths */
-       prepend_include_path("/usr/include");
-
-       /* parse environment variable */
-       const char *cpath = getenv("CPATH");
-       if (cpath != NULL && *cpath != '\0') {
-               const char *begin = cpath;
+       const char *val = getenv(envvar);
+       if (val != NULL && *val != '\0') {
+               const char *begin = val;
                const char *c;
                do {
                        c = begin;
@@ -2529,12 +2665,12 @@ static void setup_include_path(void)
 
                        size_t len = c-begin;
                        if (len == 0) {
-                               /* for gcc compatibility (Matze: I would expect that
+                               /* use "." for gcc compatibility (Matze: I would expect that
                                 * nothing happens for an empty entry...) */
-                               prepend_include_path(".");
+                               append_include_path(paths, ".");
                        } else {
                                char *const string = obstack_copy0(&config_obstack, begin, len);
-                               prepend_include_path(string);
+                               append_include_path(paths, string);
                        }
 
                        begin = c+1;
@@ -2545,6 +2681,27 @@ static void setup_include_path(void)
        }
 }
 
+static void append_searchpath(searchpath_t *path, const searchpath_t *append)
+{
+       *path->anchor = append->first;
+}
+
+static void setup_include_path(void)
+{
+       /* built-in paths */
+       append_include_path(&system_searchpath, "/usr/include");
+
+       /* parse environment variable */
+       append_env_paths(&bracket_searchpath, "CPATH");
+       append_env_paths(&system_searchpath,
+                        c_mode & _CXX ? "CPLUS_INCLUDE_PATH" : "C_INCLUDE_PATH");
+
+       /* append system search path to bracket searchpath */
+       append_searchpath(&system_searchpath,  &after_searchpath);
+       append_searchpath(&bracket_searchpath, &system_searchpath);
+       append_searchpath(&quote_searchpath, &bracket_searchpath);
+}
+
 static void input_error(unsigned const delta_lines, unsigned const delta_cols, char const *const message)
 {
        source_position_t pos = pp_token.base.source_position;
@@ -2553,11 +2710,15 @@ static void input_error(unsigned const delta_lines, unsigned const delta_cols, c
        errorf(&pos, "%s", message);
 }
 
+void init_include_paths(void)
+{
+       obstack_init(&config_obstack);
+}
+
 void init_preprocessor(void)
 {
        init_symbols();
 
-       obstack_init(&config_obstack);
        obstack_init(&pp_obstack);
        obstack_init(&input_obstack);
        strset_init(&stringset);
@@ -2580,6 +2741,7 @@ int pptest_main(int argc, char **argv);
 int pptest_main(int argc, char **argv)
 {
        init_symbol_table();
+       init_include_paths();
        init_preprocessor();
        init_tokens();
 
@@ -2592,7 +2754,7 @@ int pptest_main(int argc, char **argv)
        for (int i = 1; i < argc; ++i) {
                const char *opt = argv[i];
                if (streq(opt, "-I")) {
-                       prepend_include_path(argv[++i]);
+                       append_include_path(&bracket_searchpath, argv[++i]);
                        continue;
                } else if (streq(opt, "-E")) {
                        /* ignore */
@@ -2632,7 +2794,7 @@ int pptest_main(int argc, char **argv)
                fprintf(stderr, "Couldn't open input '%s'\n", filename);
                return 1;
        }
-       switch_pp_input(file, filename, NULL);
+       switch_pp_input(file, filename, NULL, false);
 
        for (;;) {
                next_preprocessing_token();