typedef struct gnu_attribute_t gnu_attribute_t;
struct gnu_attribute_t {
- gnu_attribute_kind_t kind; /**< The kind of the GNU attribute. */
+ gnu_attribute_kind_t kind; /**< The kind of the GNU attribute. */
gnu_attribute_t *next;
- bool invalid; /**< Set if this attribute had argument errors, */
- bool have_arguments; /**< True, if this attribute has arguments. */
+ bool invalid; /**< Set if this attribute had argument errors, */
+ bool has_arguments; /**< True, if this attribute has arguments. */
union {
size_t value;
string_t string;
- atomic_type_kind_t akind;
+ symbol_t *symbol;
long argument; /**< Single argument. */
argument_list_t *arguments; /**< List of argument expressions. */
} u;
return result;
}
-/**
- * Free a type from the type obstack.
- */
-static void free_type(void *type)
-{
- obstack_free(type_obst, type);
-}
-
/**
* Returns the index of the top element of the environment stack.
*/
next_token();
}
-#define eat(token_type) do { assert(token.type == (token_type)); next_token(); } while (0)
+#define eat(token_type) (assert(token.type == (token_type)), next_token())
/**
* Report a parse error because an expected token was not found.
static int get_rank(const type_t *type)
{
assert(!is_typeref(type));
- /* The C-standard allows promoting enums to int or unsigned int (see § 7.2.2
- * and esp. footnote 108). However we can't fold constants (yet), so we
- * can't decide whether unsigned int is possible, while int always works.
- * (unsigned int would be preferable when possible... for stuff like
- * struct { enum { ... } bla : 4; } ) */
if (type->kind == TYPE_ENUM)
- return get_akind_rank(ATOMIC_TYPE_INT);
+ return get_akind_rank(type->enumt.akind);
assert(type->kind == TYPE_ATOMIC);
return get_akind_rank(type->atomic.akind);
static bool is_null_pointer_constant(const expression_t *expression)
{
/* skip void* cast */
- if (expression->kind == EXPR_UNARY_CAST
- || expression->kind == EXPR_UNARY_CAST_IMPLICIT) {
- expression = expression->unary.value;
+ if (expression->kind == EXPR_UNARY_CAST ||
+ expression->kind == EXPR_UNARY_CAST_IMPLICIT) {
+ type_t *const type = skip_typeref(expression->base.type);
+ if (types_compatible(type, type_void_ptr))
+ expression = expression->unary.value;
}
- /* TODO: not correct yet, should be any constant integer expression
- * which evaluates to 0 */
- if (expression->kind != EXPR_CONST)
- return false;
-
type_t *const type = skip_typeref(expression->base.type);
- if (!is_type_integer(type))
- return false;
-
- return expression->conste.v.int_value == 0;
+ return
+ is_type_integer(type) &&
+ is_constant_expression(expression) &&
+ fold_constant(expression) == 0;
}
/**
attribute->kind = kind;
attribute->next = NULL;
attribute->invalid = false;
- attribute->have_arguments = false;
+ attribute->has_arguments = false;
return attribute;
}
*/
static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
{
- /* TODO: find out what is allowed here... */
-
- /* at least: byte, word, pointer, list of machine modes
- * __XXX___ is interpreted as XXX */
add_anchor_token(')');
if (token.type != T_IDENTIFIER) {
expect(T_IDENTIFIER, end_error);
}
- /* This isn't really correct, the backend should provide a list of machine
- * specific modes (according to gcc philosophy that is...) */
- const char *symbol_str = token.v.symbol->string;
- if (strcmp_underscore("QI", symbol_str) == 0 ||
- strcmp_underscore("byte", symbol_str) == 0) {
- attribute->u.akind = ATOMIC_TYPE_CHAR;
- } else if (strcmp_underscore("HI", symbol_str) == 0) {
- attribute->u.akind = ATOMIC_TYPE_SHORT;
- } else if (strcmp_underscore("SI", symbol_str) == 0
- || strcmp_underscore("word", symbol_str) == 0
- || strcmp_underscore("pointer", symbol_str) == 0) {
- attribute->u.akind = ATOMIC_TYPE_INT;
- } else if (strcmp_underscore("DI", symbol_str) == 0) {
- attribute->u.akind = ATOMIC_TYPE_LONGLONG;
- } else {
- if (warning.other)
- warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
- attribute->invalid = true;
- }
+ attribute->u.symbol = token.v.symbol;
next_token();
rem_anchor_token(')');
*/
static void check_no_argument(gnu_attribute_t *attribute, const char *name)
{
- if (!attribute->have_arguments)
+ if (!attribute->has_arguments)
return;
/* should have no arguments */
/* empty args are allowed */
next_token();
} else
- attribute->have_arguments = true;
+ attribute->has_arguments = true;
}
switch (kind) {
case GNU_AK_ALIGNED:
/* __align__ may be used without an argument */
- if (attribute->have_arguments) {
+ if (attribute->has_arguments) {
parse_gnu_attribute_const_arg(attribute);
}
break;
case GNU_AK_FORMAT_ARG:
case GNU_AK_REGPARM:
case GNU_AK_TRAP_EXIT:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
case GNU_AK_ALIAS:
case GNU_AK_SECTION:
case GNU_AK_SP_SWITCH:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
break;
case GNU_AK_FORMAT:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
attribute->invalid = true;
break;
case GNU_AK_WEAKREF:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
break;
case GNU_AK_NONNULL:
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_const_arg_list(attribute);
break;
case GNU_AK_TLS_MODEL:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else
parse_gnu_attribute_tls_model_arg(attribute);
break;
case GNU_AK_VISIBILITY:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else
parse_gnu_attribute_visibility_arg(attribute);
break;
case GNU_AK_MODEL:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else {
}
break;
case GNU_AK_MODE:
- if (!attribute->have_arguments) {
+ if (!attribute->has_arguments) {
/* should have arguments */
errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
} else {
break;
case GNU_AK_INTERRUPT:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_interrupt_arg(attribute);
break;
case GNU_AK_SENTINEL:
/* may have one string argument */
- if (attribute->have_arguments)
+ if (attribute->has_arguments)
parse_gnu_attribute_const_arg(attribute);
break;
case GNU_AK_LAST:
result = parse_scalar_initializer(type, env->must_be_constant);
}
- /* § 6.7.8 (22) array initializers for arrays with unknown size determine
+ /* § 6.7.8:22 array initializers for arrays with unknown size determine
* the array type size */
if (is_type_array(type) && type->array.size_expression == NULL
&& result != NULL) {
compound = &entity->compound;
if (compound->base.parent_scope != current_scope &&
(token.type == '{' || token.type == ';')) {
- /* we're in an inner scope and have a definition. Override
- existing definition in outer scope */
+ /* we're in an inner scope and have a definition. Shadow
+ * existing definition in outer scope */
compound = NULL;
} else if (compound->complete && token.type == '{') {
assert(symbol != NULL);
next_token();
entity = get_entity(symbol, NAMESPACE_ENUM);
- assert(entity == NULL || entity->kind == ENTITY_ENUM);
+ if (entity != NULL) {
+ assert(entity->kind == ENTITY_ENUM);
+ if (entity->base.parent_scope != current_scope &&
+ (token.type == '{' || token.type == ';')) {
+ /* we're in an inner scope and have a definition. Shadow
+ * existing definition in outer scope */
+ entity = NULL;
+ } else if (entity->enume.complete && token.type == '{') {
+ errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
+ symbol, &entity->base.source_position);
+ }
+ }
} else if (token.type != '{') {
parse_error_expected("while parsing enum type specifier",
T_IDENTIFIER, '{', NULL);
type_t *const type = allocate_type_zero(TYPE_ENUM);
type->enumt.enume = &entity->enume;
+ type->enumt.akind = ATOMIC_TYPE_INT;
if (token.type == '{') {
- if (entity->enume.complete) {
- errorf(HERE, "multiple definitions of 'enum %Y' (previous definition %P)",
- symbol, &entity->base.source_position);
- }
if (symbol != NULL) {
environment_push(entity);
}
type_t *type = allocate_type_zero(TYPE_BUILTIN);
type->builtin.symbol = symbol;
type->builtin.real_type = real_type;
-
- type_t *result = typehash_insert(type);
- if (type != result) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
static type_t *get_typedef_type(symbol_t *symbol)
type->base.alignment = alignment;
}
+static type_t *handle_attribute_mode(const gnu_attribute_t *attribute,
+ type_t *orig_type)
+{
+ type_t *type = skip_typeref(orig_type);
+
+ /* at least: byte, word, pointer, list of machine modes
+ * __XXX___ is interpreted as XXX */
+
+ /* This isn't really correct, the backend should provide a list of machine
+ * specific modes (according to gcc philosophy that is...) */
+ const char *symbol_str = attribute->u.symbol->string;
+ bool sign = is_type_signed(type);
+ atomic_type_kind_t akind;
+ if (strcmp_underscore("QI", symbol_str) == 0 ||
+ strcmp_underscore("byte", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_CHAR : ATOMIC_TYPE_UCHAR;
+ } else if (strcmp_underscore("HI", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_SHORT : ATOMIC_TYPE_USHORT;
+ } else if (strcmp_underscore("SI", symbol_str) == 0
+ || strcmp_underscore("word", symbol_str) == 0
+ || strcmp_underscore("pointer", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_INT : ATOMIC_TYPE_UINT;
+ } else if (strcmp_underscore("DI", symbol_str) == 0) {
+ akind = sign ? ATOMIC_TYPE_LONGLONG : ATOMIC_TYPE_ULONGLONG;
+ } else {
+ if (warning.other)
+ warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
+ return orig_type;
+ }
+
+ if (type->kind == TYPE_ATOMIC) {
+ type_t *copy = duplicate_type(type);
+ copy->atomic.akind = akind;
+ return identify_new_type(copy);
+ } else if (type->kind == TYPE_ENUM) {
+ type_t *copy = duplicate_type(type);
+ copy->enumt.akind = akind;
+ return identify_new_type(copy);
+ } else if (is_type_pointer(type)) {
+ warningf(HERE, "__attribute__((mode)) on pointers not implemented yet (ignored)");
+ return type;
+ }
+
+ errorf(HERE, "__attribute__((mode)) only allowed on integer, enum or pointer type");
+ return orig_type;
+}
+
+static type_t *handle_type_attributes(const gnu_attribute_t *attributes,
+ type_t *type)
+{
+ const gnu_attribute_t *attribute = attributes;
+ for ( ; attribute != NULL; attribute = attribute->next) {
+ if (attribute->invalid)
+ continue;
+
+ if (attribute->kind == GNU_AK_MODE) {
+ type = handle_attribute_mode(attribute, type);
+ } else if (attribute->kind == GNU_AK_ALIGNED) {
+ int alignment = 32; /* TODO: fill in maximum useful alignment for
+ target machine */
+ if (attribute->has_arguments)
+ alignment = attribute->u.argument;
+
+ type_t *copy = duplicate_type(type);
+ copy->base.alignment = attribute->u.argument;
+ type = identify_new_type(copy);
+ }
+ }
+
+ return type;
+}
+
static void parse_declaration_specifiers(declaration_specifiers_t *specifiers)
{
type_t *type = NULL;
while (true) {
specifiers->modifiers
|= parse_attributes(&specifiers->gnu_attributes);
- if (specifiers->modifiers & DM_TRANSPARENT_UNION)
- modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
switch (token.type) {
/* storage class */
}
finish_specifiers:
+ specifiers->modifiers
+ |= parse_attributes(&specifiers->gnu_attributes);
+
in_gcc_extension = old_gcc_extension;
if (type == NULL || (saw_error && type_specifiers != 0)) {
type = allocate_type_zero(TYPE_IMAGINARY);
type->imaginary.akind = atomic_type;
} else {
- type = allocate_type_zero(TYPE_ATOMIC);
- type->atomic.akind = atomic_type;
+ type = allocate_type_zero(TYPE_ATOMIC);
+ type->atomic.akind = atomic_type;
}
+ type->base.alignment = get_atomic_type_alignment(atomic_type);
+ unsigned const size = get_atomic_type_size(atomic_type);
+ type->base.size =
+ type_specifiers & SPECIFIER_COMPLEX ? size * 2 : size;
newtype = true;
} else if (type_specifiers != 0) {
errorf(HERE, "multiple datatypes in declaration");
/* FIXME: check type qualifiers here */
+ if (specifiers->modifiers & DM_TRANSPARENT_UNION)
+ modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
type->base.qualifiers = qualifiers;
type->base.modifiers = modifiers;
- type_t *result = typehash_insert(type);
- if (newtype && result != type) {
- free_type(type);
- }
+ type = identify_new_type(type);
- specifiers->type = result;
+ type = handle_type_attributes(specifiers->gnu_attributes, type);
+ specifiers->type = type;
return;
end_error:
if (type == NULL)
return;
- /* handle these strange/stupid mode attributes */
gnu_attribute_t *attribute = attributes;
for ( ; attribute != NULL; attribute = attribute->next) {
- if (attribute->kind != GNU_AK_MODE || attribute->invalid)
+ if (attribute->invalid)
continue;
- atomic_type_kind_t akind = attribute->u.akind;
- if (!is_type_signed(type)) {
- switch (akind) {
- case ATOMIC_TYPE_CHAR: akind = ATOMIC_TYPE_UCHAR; break;
- case ATOMIC_TYPE_SHORT: akind = ATOMIC_TYPE_USHORT; break;
- case ATOMIC_TYPE_INT: akind = ATOMIC_TYPE_UINT; break;
- case ATOMIC_TYPE_LONGLONG: akind = ATOMIC_TYPE_ULONGLONG; break;
- default:
- panic("invalid akind in mode attribute");
- }
- } else {
- switch (akind) {
- case ATOMIC_TYPE_CHAR: akind = ATOMIC_TYPE_SCHAR; break;
- case ATOMIC_TYPE_SHORT: akind = ATOMIC_TYPE_SHORT; break;
- case ATOMIC_TYPE_INT: akind = ATOMIC_TYPE_INT; break;
- case ATOMIC_TYPE_LONGLONG: akind = ATOMIC_TYPE_LONGLONG; break;
- default:
- panic("invalid akind in mode attribute");
+ if (attribute->kind == GNU_AK_MODE) {
+ type = handle_attribute_mode(attribute, type);
+ } else if (attribute->kind == GNU_AK_ALIGNED) {
+ int alignment = 32; /* TODO: fill in maximum usefull alignment for target machine */
+ if (attribute->has_arguments)
+ alignment = attribute->u.argument;
+
+ if (entity->kind == ENTITY_TYPEDEF) {
+ type_t *copy = duplicate_type(type);
+ copy->base.alignment = attribute->u.argument;
+ type = identify_new_type(copy);
+ } else if(entity->kind == ENTITY_VARIABLE) {
+ entity->variable.alignment = alignment;
+ } else if(entity->kind == ENTITY_COMPOUND_MEMBER) {
+ entity->compound_member.alignment = alignment;
}
}
-
- type = make_atomic_type(akind, type->base.qualifiers);
}
type_modifiers_t type_modifiers = type->base.modifiers;
type_modifiers |= TYPE_MODIFIER_TRANSPARENT_UNION;
if (type->base.modifiers != type_modifiers) {
- type_t *copy = duplicate_type(type);
+ type_t *copy = duplicate_type(type);
copy->base.modifiers = type_modifiers;
-
- type = typehash_insert(copy);
- if (type != copy) {
- obstack_free(type_obst, copy);
- }
+ type = identify_new_type(copy);
}
if (entity->kind == ENTITY_TYPEDEF) {
function_type->function.return_type = type;
type_t *skipped_return_type = skip_typeref(type);
- /* §6.7.5.3(1) */
+ /* §6.7.5.3:1 */
if (is_type_function(skipped_return_type)) {
errorf(HERE, "function returning function is not allowed");
} else if (is_type_array(skipped_return_type)) {
}
type_t *skipped_type = skip_typeref(type);
- /* §6.7.5.2(1) */
+ /* §6.7.5.2:1 */
if (is_type_incomplete(skipped_type)) {
errorf(HERE, "array of incomplete type '%T' is not allowed", type);
} else if (is_type_function(skipped_type)) {
}
}
- type_t *hashed_type = typehash_insert(type);
- if (hashed_type != type) {
- /* the function type was constructed earlier freeing it here will
- * destroy other types... */
- if (iter->kind != CONSTRUCT_FUNCTION) {
- free_type(type);
- }
- type = hashed_type;
+ /* The function type was constructed earlier. Freeing it here will
+ * destroy other types. */
+ if (iter->kind == CONSTRUCT_FUNCTION) {
+ type = typehash_insert(type);
+ } else {
+ type = identify_new_type(type);
}
}
entity->variable.get_property_sym = specifiers->get_property_sym;
entity->variable.put_property_sym = specifiers->put_property_sym;
- if (specifiers->alignment != 0) {
- /* TODO: add checks here */
- entity->variable.alignment = specifiers->alignment;
- }
entity->variable.thread_local = specifiers->thread_local;
current_init_decl = NULL;
if (entity->kind == ENTITY_VARIABLE) {
- /* § 6.7.5 (22) array initializers for arrays with unknown size
+ /* § 6.7.5:22 array initializers for arrays with unknown size
* determine the array type size */
declaration->type = env.type;
entity->variable.initializer = initializer;
new_type->function.parameters = parameters;
new_type->function.unspecified_parameters = true;
- type = typehash_insert(new_type);
- if (type != new_type) {
- obstack_free(type_obst, new_type);
- }
+ new_type = identify_new_type(new_type);
- entity->declaration.type = type;
+ entity->declaration.type = new_type;
rem_anchor_token('{');
}
ndeclaration->base.symbol);
}
- /* § 6.7.5.3 (14) a function definition with () means no
+ /* § 6.7.5.3:14 a function definition with () means no
* parameters (and not unspecified parameters) */
- if (type->function.unspecified_parameters
- && type->function.parameters == NULL
- && !type->function.kr_style_parameters) {
- type_t *duplicate = duplicate_type(type);
- duplicate->function.unspecified_parameters = false;
+ if (type->function.unspecified_parameters &&
+ type->function.parameters == NULL &&
+ !type->function.kr_style_parameters) {
+ type_t *copy = duplicate_type(type);
+ copy->function.unspecified_parameters = false;
+ type = identify_new_type(copy);
- type = typehash_insert(duplicate);
- if (type != duplicate) {
- obstack_free(type_obst, duplicate);
- }
ndeclaration->declaration.type = type;
}
ntype->function.return_type = type_int;
ntype->function.unspecified_parameters = true;
ntype->function.linkage = LINKAGE_C;
-
- type_t *type = typehash_insert(ntype);
- if (type != ntype) {
- free_type(ntype);
- }
+ type_t *type = identify_new_type(ntype);
entity_t *entity = allocate_entity_zero(ENTITY_FUNCTION);
entity->declaration.storage_class = STORAGE_CLASS_EXTERN;
type->function.return_type = return_type;
type->function.parameters = parameter1;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
/**
type->function.return_type = return_type;
type->function.parameters = parameter;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
static type_t *make_function_0_type(type_t *return_type)
type->function.return_type = return_type;
type->function.parameters = NULL;
- type_t *result = typehash_insert(type);
- if (result != type) {
- free_type(type);
- }
-
- return result;
+ return identify_new_type(type);
}
/**
static bool is_lvalue(const expression_t *expression)
{
- /* TODO: doesn't seem to be consistent with §6.3.2.1 (1) */
+ /* TODO: doesn't seem to be consistent with §6.3.2.1:1 */
switch (expression->kind) {
case EXPR_ARRAY_ACCESS:
case EXPR_COMPOUND_LITERAL:
new_type->array.has_implicit_size = true;
new_type->array.size = 1;
- type_t *const result = typehash_insert(new_type);
- if (type != result)
- free_type(type);
+ type_t *const result = identify_new_type(new_type);
decl->type = result;
}