+ } while(token.type == T___extension__);
+ goto restart;
+
+ case T_IDENTIFIER:
+ if(is_typedef_symbol(token.v.symbol)) {
+ type = parse_typename();
+ } else {
+ expression = parse_expression();
+ type = expression->base.type;
+ }
+ break;
+
+ TYPENAME_START
+ type = parse_typename();
+ break;
+
+ default:
+ expression = parse_expression();
+ type = expression->base.type;
+ break;
+ }
+
+ rem_anchor_token(')');
+ expect(')');
+
+ type_t *typeof_type = allocate_type_zero(TYPE_TYPEOF, &expression->base.source_position);
+ typeof_type->typeoft.expression = expression;
+ typeof_type->typeoft.typeof_type = type;
+
+ return typeof_type;
+end_error:
+ return NULL;
+}
+
+typedef enum {
+ SPECIFIER_SIGNED = 1 << 0,
+ SPECIFIER_UNSIGNED = 1 << 1,
+ SPECIFIER_LONG = 1 << 2,
+ SPECIFIER_INT = 1 << 3,
+ SPECIFIER_DOUBLE = 1 << 4,
+ SPECIFIER_CHAR = 1 << 5,
+ SPECIFIER_SHORT = 1 << 6,
+ SPECIFIER_LONG_LONG = 1 << 7,
+ SPECIFIER_FLOAT = 1 << 8,
+ SPECIFIER_BOOL = 1 << 9,
+ SPECIFIER_VOID = 1 << 10,
+ SPECIFIER_INT8 = 1 << 11,
+ SPECIFIER_INT16 = 1 << 12,
+ SPECIFIER_INT32 = 1 << 13,
+ SPECIFIER_INT64 = 1 << 14,
+ SPECIFIER_INT128 = 1 << 15,
+ SPECIFIER_COMPLEX = 1 << 16,
+ SPECIFIER_IMAGINARY = 1 << 17,
+} specifiers_t;
+
+static type_t *create_builtin_type(symbol_t *const symbol,
+ type_t *const real_type)
+{
+ type_t *type = allocate_type_zero(TYPE_BUILTIN, &builtin_source_position);
+ type->builtin.symbol = symbol;
+ type->builtin.real_type = real_type;
+
+ type_t *result = typehash_insert(type);
+ if(type != result) {
+ free_type(type);
+ }
+
+ return result;
+}
+
+static type_t *get_typedef_type(symbol_t *symbol)
+{
+ declaration_t *declaration = get_declaration(symbol, NAMESPACE_NORMAL);
+ if(declaration == NULL ||
+ declaration->storage_class != STORAGE_CLASS_TYPEDEF)
+ return NULL;
+
+ type_t *type = allocate_type_zero(TYPE_TYPEDEF, &declaration->source_position);
+ type->typedeft.declaration = declaration;
+
+ return type;
+}
+
+/**
+ * check for the allowed MS alignment values.
+ */
+static bool check_elignment_value(long long intvalue) {
+ if(intvalue < 1 || intvalue > 8192) {
+ errorf(HERE, "illegal alignment value");
+ return false;
+ }
+ unsigned v = (unsigned)intvalue;
+ for(unsigned i = 1; i <= 8192; i += i) {
+ if (i == v)
+ return true;
+ }
+ errorf(HERE, "alignment must be power of two");
+ return false;
+}
+
+#define DET_MOD(name, tag) do { \
+ if(*modifiers & tag) warningf(HERE, #name " used more than once"); \
+ *modifiers |= tag; \
+} while(0)
+
+static void parse_microsoft_extended_decl_modifier(declaration_specifiers_t *specifiers)
+{
+ decl_modifiers_t *modifiers = &specifiers->decl_modifiers;
+
+ while(true) {
+ if(token.type == T_restrict) {
+ next_token();
+ DET_MOD(restrict, DM_RESTRICT);
+ goto end_loop;
+ } else if(token.type != T_IDENTIFIER)
+ break;
+ symbol_t *symbol = token.v.symbol;
+ if(symbol == sym_align) {
+ next_token();
+ expect('(');
+ if(token.type != T_INTEGER)
+ goto end_error;
+ if(check_elignment_value(token.v.intvalue)) {
+ if(specifiers->alignment != 0)
+ warningf(HERE, "align used more than once");
+ specifiers->alignment = (unsigned char)token.v.intvalue;
+ }
+ next_token();
+ expect(')');
+ } else if(symbol == sym_allocate) {
+ next_token();
+ expect('(');
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ (void)token.v.symbol;
+ expect(')');
+ } else if(symbol == sym_dllimport) {
+ next_token();
+ DET_MOD(dllimport, DM_DLLIMPORT);
+ } else if(symbol == sym_dllexport) {
+ next_token();
+ DET_MOD(dllexport, DM_DLLEXPORT);
+ } else if(symbol == sym_thread) {
+ next_token();
+ DET_MOD(thread, DM_THREAD);
+ } else if(symbol == sym_naked) {
+ next_token();
+ DET_MOD(naked, DM_NAKED);
+ } else if(symbol == sym_noinline) {
+ next_token();
+ DET_MOD(noinline, DM_NOINLINE);
+ } else if(symbol == sym_noreturn) {
+ next_token();
+ DET_MOD(noreturn, DM_NORETURN);
+ } else if(symbol == sym_nothrow) {
+ next_token();
+ DET_MOD(nothrow, DM_NOTHROW);
+ } else if(symbol == sym_novtable) {
+ next_token();
+ DET_MOD(novtable, DM_NOVTABLE);
+ } else if(symbol == sym_property) {
+ next_token();
+ expect('(');
+ for(;;) {
+ bool is_get = false;
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ if(token.v.symbol == sym_get) {
+ is_get = true;
+ } else if(token.v.symbol == sym_put) {
+ } else {
+ errorf(HERE, "Bad property name '%Y'", token.v.symbol);
+ goto end_error;
+ }
+ next_token();
+ expect('=');
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ if(is_get) {
+ if(specifiers->get_property_sym != NULL) {
+ errorf(HERE, "get property name already specified");
+ } else {
+ specifiers->get_property_sym = token.v.symbol;
+ }
+ } else {
+ if(specifiers->put_property_sym != NULL) {
+ errorf(HERE, "put property name already specified");
+ } else {
+ specifiers->put_property_sym = token.v.symbol;
+ }
+ }
+ next_token();
+ if(token.type == ',') {
+ next_token();
+ continue;
+ }
+ break;
+ }
+ expect(')');
+ } else if(symbol == sym_selectany) {
+ next_token();
+ DET_MOD(selectany, DM_SELECTANY);
+ } else if(symbol == sym_uuid) {
+ next_token();
+ expect('(');
+ if(token.type != T_STRING_LITERAL)
+ goto end_error;
+ next_token();
+ expect(')');
+ } else if(symbol == sym_deprecated) {
+ next_token();
+ if(specifiers->deprecated != 0)
+ warningf(HERE, "deprecated used more than once");
+ specifiers->deprecated = 1;
+ if(token.type == '(') {
+ next_token();
+ if(token.type == T_STRING_LITERAL) {
+ specifiers->deprecated_string = token.v.string.begin;
+ next_token();
+ } else {
+ errorf(HERE, "string literal expected");
+ }
+ expect(')');
+ }
+ } else if(symbol == sym_noalias) {
+ next_token();
+ DET_MOD(noalias, DM_NOALIAS);