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;
static const symbol_t *sym_anonymous = NULL;
/* symbols for Microsoft extended-decl-modifier */
-static const symbol_t *sym_align = NULL;
-static const symbol_t *sym_allocate = NULL;
-static const symbol_t *sym_dllimport = NULL;
-static const symbol_t *sym_dllexport = NULL;
-static const symbol_t *sym_naked = NULL;
-static const symbol_t *sym_noinline = NULL;
-static const symbol_t *sym_noreturn = NULL;
-static const symbol_t *sym_nothrow = NULL;
-static const symbol_t *sym_novtable = NULL;
-static const symbol_t *sym_property = NULL;
-static const symbol_t *sym_get = NULL;
-static const symbol_t *sym_put = NULL;
-static const symbol_t *sym_selectany = NULL;
-static const symbol_t *sym_thread = NULL;
-static const symbol_t *sym_uuid = NULL;
-static const symbol_t *sym_deprecated = NULL;
-static const symbol_t *sym_restrict = NULL;
-static const symbol_t *sym_noalias = NULL;
+static const symbol_t *sym_align = NULL;
+static const symbol_t *sym_allocate = NULL;
+static const symbol_t *sym_dllimport = NULL;
+static const symbol_t *sym_dllexport = NULL;
+static const symbol_t *sym_naked = NULL;
+static const symbol_t *sym_noinline = NULL;
+static const symbol_t *sym_returns_twice = NULL;
+static const symbol_t *sym_noreturn = NULL;
+static const symbol_t *sym_nothrow = NULL;
+static const symbol_t *sym_novtable = NULL;
+static const symbol_t *sym_property = NULL;
+static const symbol_t *sym_get = NULL;
+static const symbol_t *sym_put = NULL;
+static const symbol_t *sym_selectany = NULL;
+static const symbol_t *sym_thread = NULL;
+static const symbol_t *sym_uuid = NULL;
+static const symbol_t *sym_deprecated = NULL;
+static const symbol_t *sym_restrict = NULL;
+static const symbol_t *sym_noalias = NULL;
/** The token anchor set */
static unsigned char token_anchor_set[T_LAST_TOKEN];
[STATEMENT_COMPOUND] = sizeof(compound_statement_t),
[STATEMENT_RETURN] = sizeof(return_statement_t),
[STATEMENT_DECLARATION] = sizeof(declaration_statement_t),
- [STATEMENT_LOCAL_LABEL] = sizeof(local_label_statement_t),
[STATEMENT_IF] = sizeof(if_statement_t),
[STATEMENT_SWITCH] = sizeof(switch_statement_t),
[STATEMENT_EXPRESSION] = sizeof(expression_statement_t),
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;
}
/**
[GNU_AK_FASTCALL] = "fastcall",
[GNU_AK_DEPRECATED] = "deprecated",
[GNU_AK_NOINLINE] = "noinline",
+ [GNU_AK_RETURNS_TWICE] = "returns_twice",
[GNU_AK_NORETURN] = "noreturn",
[GNU_AK_NAKED] = "naked",
[GNU_AK_PURE] = "pure",
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_DLLEXPORT: modifiers |= DM_DLLEXPORT; goto no_arg;
case GNU_AK_PACKED: modifiers |= DM_PACKED; goto no_arg;
case GNU_AK_NOINLINE: modifiers |= DM_NOINLINE; goto no_arg;
+ case GNU_AK_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; goto no_arg;
case GNU_AK_NORETURN: modifiers |= DM_NORETURN; goto no_arg;
case GNU_AK_NOTHROW: modifiers |= DM_NOTHROW; goto no_arg;
case GNU_AK_TRANSPARENT_UNION: modifiers |= DM_TRANSPARENT_UNION; goto no_arg;
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:
} else {
/* must be an expression */
expression_t *expression = parse_assignment_expression();
+ mark_vars_read(expression, NULL);
if (env->must_be_constant && !is_initializer_constant(expression)) {
errorf(&expression->base.source_position,
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)
} else if (symbol == sym_noinline) {
next_token();
DET_MOD(noinline, DM_NOINLINE);
+ } else if (symbol == sym_returns_twice) {
+ next_token();
+ DET_MOD(returns_twice, DM_RETURNS_TWICE);
} else if (symbol == sym_noreturn) {
next_token();
DET_MOD(noreturn, DM_NORETURN);
entity->typedefe.type = type_error_type;
entity->typedefe.builtin = true;
}
- record_entity(entity, false);
+ if (kind != ENTITY_COMPOUND_MEMBER)
+ record_entity(entity, false);
return entity;
}
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:
array->is_variable = true;
next_token();
} else if (token.type != ']') {
- array->size = parse_assignment_expression();
+ expression_t *const size = parse_assignment_expression();
+ array->size = size;
+ mark_vars_read(size, NULL);
}
rem_anchor_token(']');
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);
}
}
if (flags & DECL_CREATE_COMPOUND_MEMBER) {
entity = allocate_entity_zero(ENTITY_COMPOUND_MEMBER);
- if (specifiers->is_inline && is_type_valid(type)) {
- errorf(&env.source_position,
- "compound member '%Y' declared 'inline'", env.symbol);
- }
+ if (env.symbol != NULL) {
+ if (specifiers->is_inline && is_type_valid(type)) {
+ errorf(&env.source_position,
+ "compound member '%Y' declared 'inline'", env.symbol);
+ }
- if (specifiers->thread_local ||
- specifiers->storage_class != STORAGE_CLASS_NONE) {
- errorf(&env.source_position,
- "compound member '%Y' must have no storage class",
- env.symbol);
+ if (specifiers->thread_local ||
+ specifiers->storage_class != STORAGE_CLASS_NONE) {
+ errorf(&env.source_position,
+ "compound member '%Y' must have no storage class",
+ env.symbol);
+ }
}
} else if (flags & DECL_IS_PARAMETER) {
orig_type = semantic_parameter(&env.source_position, orig_type,
entity->function.is_inline = specifiers->is_inline;
entity->function.parameters = env.parameters;
- if (specifiers->thread_local || (
- specifiers->storage_class != STORAGE_CLASS_EXTERN &&
- specifiers->storage_class != STORAGE_CLASS_NONE &&
- specifiers->storage_class != STORAGE_CLASS_STATIC)
- ) {
- errorf(&env.source_position,
- "invalid storage class for function '%Y'", env.symbol);
+ if (env.symbol != NULL) {
+ if (specifiers->thread_local || (
+ specifiers->storage_class != STORAGE_CLASS_EXTERN &&
+ specifiers->storage_class != STORAGE_CLASS_NONE &&
+ specifiers->storage_class != STORAGE_CLASS_STATIC
+ )) {
+ errorf(&env.source_position,
+ "invalid storage class for function '%Y'", env.symbol);
+ }
}
} else {
entity = allocate_entity_zero(ENTITY_VARIABLE);
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;
- }
-
- if (specifiers->is_inline && is_type_valid(type)) {
- errorf(&env.source_position,
- "variable '%Y' declared 'inline'", env.symbol);
- }
entity->variable.thread_local = specifiers->thread_local;
- bool invalid_storage_class = false;
- if (current_scope == file_scope) {
- if (specifiers->storage_class != STORAGE_CLASS_EXTERN &&
- specifiers->storage_class != STORAGE_CLASS_NONE &&
- specifiers->storage_class != STORAGE_CLASS_STATIC) {
- invalid_storage_class = true;
+ if (env.symbol != NULL) {
+ if (specifiers->is_inline && is_type_valid(type)) {
+ errorf(&env.source_position,
+ "variable '%Y' declared 'inline'", env.symbol);
}
- } else {
- if (specifiers->thread_local &&
- specifiers->storage_class == STORAGE_CLASS_NONE) {
- invalid_storage_class = true;
+
+ bool invalid_storage_class = false;
+ if (current_scope == file_scope) {
+ if (specifiers->storage_class != STORAGE_CLASS_EXTERN &&
+ specifiers->storage_class != STORAGE_CLASS_NONE &&
+ specifiers->storage_class != STORAGE_CLASS_STATIC) {
+ invalid_storage_class = true;
+ }
+ } else {
+ if (specifiers->thread_local &&
+ specifiers->storage_class == STORAGE_CLASS_NONE) {
+ invalid_storage_class = true;
+ }
+ }
+ if (invalid_storage_class) {
+ errorf(&env.source_position,
+ "invalid storage class for variable '%Y'", env.symbol);
}
- }
- if (invalid_storage_class) {
- errorf(&env.source_position,
- "invalid storage class for variable '%Y'", env.symbol);
}
}
- entity->base.source_position = env.source_position;
- entity->base.symbol = env.symbol;
+ if (env.symbol != NULL) {
+ entity->base.symbol = env.symbol;
+ entity->base.source_position = env.source_position;
+ } else {
+ entity->base.source_position = specifiers->source_position;
+ }
entity->base.namespc = NAMESPACE_NORMAL;
entity->declaration.type = orig_type;
entity->declaration.modifiers = env.modifiers;
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('{');
}
}
}
-static void warn_unused_entity(entity_t *entity, entity_t *end)
+static void warn_unused_entity(entity_t *entity, entity_t *last)
{
- for (; entity != NULL; entity = entity->base.next) {
+ entity_t const *const end = last != NULL ? last->base.next : NULL;
+ for (; entity != end; entity = entity->base.next) {
if (!is_declaration(entity))
continue;
warningf(&entity->base.source_position, "%s '%Y' is never read",
what, entity->base.symbol);
}
-
- if (entity == end)
- break;
}
}
}
static void check_reachable(statement_t *);
+static bool reaches_end;
static bool expression_returns(expression_t const *const expr)
{
case EXPR_INVALID:
return true;
- case EXPR_STATEMENT:
+ case EXPR_STATEMENT: {
+ bool old_reaches_end = reaches_end;
+ reaches_end = false;
check_reachable(expr->statement.statement);
- // TODO check if statement can be left
- return true;
+ bool returns = reaches_end;
+ reaches_end = old_reaches_end;
+ return returns;
+ }
case EXPR_CONDITIONAL:
// TODO handle constant expression
switch (stmt->kind) {
case STATEMENT_INVALID:
case STATEMENT_EMPTY:
- case STATEMENT_LOCAL_LABEL:
case STATEMENT_ASM:
next = stmt->base.next;
break;
case STATEMENT_COMPOUND:
next = stmt->compound.statements;
+ if (next == NULL)
+ next = stmt->base.next;
break;
case STATEMENT_RETURN: {
case STATEMENT_INVALID:
case STATEMENT_EMPTY:
case STATEMENT_DECLARATION:
- case STATEMENT_LOCAL_LABEL:
case STATEMENT_EXPRESSION:
case STATEMENT_ASM:
case STATEMENT_RETURN:
panic("invalid control flow in function");
case STATEMENT_COMPOUND:
+ if (next->compound.stmt_expr) {
+ reaches_end = true;
+ return;
+ }
+ /* FALLTHROUGH */
case STATEMENT_IF:
case STATEMENT_SWITCH:
case STATEMENT_LABEL:
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;
}
entity->declaration.storage_class = STORAGE_CLASS_NONE;
entity->declaration.modifiers = specifiers->modifiers;
entity->declaration.type = type;
+ append_entity(&compound->members, entity);
} else {
entity = parse_declarator(specifiers,
DECL_MAY_BE_ABSTRACT | DECL_CREATE_COMPOUND_MEMBER);
- assert(entity->kind == ENTITY_COMPOUND_MEMBER);
-
- if (token.type == ':') {
- source_position_t source_position = *HERE;
- next_token();
- expression_t *size = parse_constant_expression();
-
- type_t *type = entity->declaration.type;
- type_t *bitfield_type = make_bitfield_type(type, size,
- &source_position, entity->base.symbol);
- entity->declaration.type = bitfield_type;
- }
- }
-
- /* make sure we don't define a symbol multiple times */
- symbol_t *symbol = entity->base.symbol;
- if (symbol != NULL) {
- entity_t *prev = find_compound_entry(compound, symbol);
-
- if (prev != NULL) {
+ if (entity->kind == ENTITY_TYPEDEF) {
errorf(&entity->base.source_position,
- "multiple declarations of symbol '%Y' (declared %P)",
- symbol, &prev->base.source_position);
- }
- }
+ "typedef not allowed as compound member");
+ } else {
+ assert(entity->kind == ENTITY_COMPOUND_MEMBER);
+
+ /* make sure we don't define a symbol multiple times */
+ symbol_t *symbol = entity->base.symbol;
+ if (symbol != NULL) {
+ entity_t *prev = find_compound_entry(compound, symbol);
+ if (prev != NULL) {
+ errorf(&entity->base.source_position,
+ "multiple declarations of symbol '%Y' (declared %P)",
+ symbol, &prev->base.source_position);
+ }
+ }
- append_entity(&compound->members, entity);
+ if (token.type == ':') {
+ source_position_t source_position = *HERE;
+ next_token();
+ expression_t *size = parse_constant_expression();
- type_t *orig_type = entity->declaration.type;
- type_t *type = skip_typeref(orig_type);
- if (is_type_function(type)) {
- errorf(&entity->base.source_position,
- "compound member '%Y' must not have function type '%T'",
- entity->base.symbol, orig_type);
- } else if (is_type_incomplete(type)) {
- /* §6.7.2.1:16 flexible array member */
- if (is_type_array(type) &&
- token.type == ';' &&
- look_ahead(1)->type == '}') {
- compound->has_flexible_member = true;
- } else {
- errorf(&entity->base.source_position,
- "compound member '%Y' has incomplete type '%T'",
- entity->base.symbol, orig_type);
+ type_t *type = entity->declaration.type;
+ type_t *bitfield_type = make_bitfield_type(type, size,
+ &source_position, entity->base.symbol);
+ entity->declaration.type = bitfield_type;
+ } else {
+ type_t *orig_type = entity->declaration.type;
+ type_t *type = skip_typeref(orig_type);
+ if (is_type_function(type)) {
+ errorf(&entity->base.source_position,
+ "compound member '%Y' must not have function type '%T'",
+ entity->base.symbol, orig_type);
+ } else if (is_type_incomplete(type)) {
+ /* §6.7.2.1:16 flexible array member */
+ if (is_type_array(type) &&
+ token.type == ';' &&
+ look_ahead(1)->type == '}') {
+ compound->has_flexible_member = true;
+ } else {
+ errorf(&entity->base.source_position,
+ "compound member '%Y' has incomplete type '%T'",
+ entity->base.symbol, orig_type);
+ }
+ }
+ }
+
+ append_entity(&compound->members, entity);
}
}
typedef struct expression_parser_function_t expression_parser_function_t;
struct expression_parser_function_t {
parse_expression_function parser;
- unsigned infix_precedence;
+ precedence_t infix_precedence;
parse_expression_infix_function infix_parser;
};
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);
}
/**
assert(is_declaration(entity));
type_t *type = entity->declaration.type;
return get_qualified_type(type,
- expression->base.type->base.qualifiers);
+ expression->base.type->base.qualifiers);
}
case EXPR_UNARY_DEREFERENCE: {
const expression_t *const value = expression->unary.value;
type_t *const type = skip_typeref(value->base.type);
- assert(is_type_pointer(type));
+ if (!is_type_pointer(type))
+ return type_error_type;
return type->pointer.points_to;
}
case EXPR_ARRAY_ACCESS: {
const expression_t *array_ref = expression->array_access.array_ref;
type_t *type_left = skip_typeref(array_ref->base.type);
- if (!is_type_valid(type_left))
- return type_left;
- assert(is_type_pointer(type_left));
+ if (!is_type_pointer(type_left))
+ return type_error_type;
return type_left->pointer.points_to;
}
case EXPR_COMPOUND_LITERAL:
return expression->compound_literal.type;
- default: break;
+ default:
+ return expression->base.type;
}
-
- return expression->base.type;
}
static expression_t *parse_reference(void)
expression_t *expression = allocate_expression_zero(EXPR_STATEMENT);
statement_t *statement = parse_compound_statement(true);
+ statement->compound.stmt_expr = true;
expression->statement.statement = statement;
/* find last statement and use its type */
type_t *const type = skip_typeref(value->base.type);
if (!is_type_pointer(type)) {
- errorf(&value->base.source_position,
- "operand of delete must have pointer type");
+ if (is_type_valid(type)) {
+ errorf(&value->base.source_position,
+ "operand of delete must have pointer type");
+ }
} else if (warning.other &&
is_type_atomic(skip_typeref(type->pointer.points_to), ATOMIC_TYPE_VOID)) {
warningf(&value->base.source_position,
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:
static void warn_addsub_in_shift(const expression_t *const expr)
{
+ if (expr->base.parenthesized)
+ return;
+
char op;
switch (expr->kind) {
case EXPR_BINARY_ADD: op = '+'; break;
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
- return;
} else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
check_pointer_arithmetic(&expression->base.source_position,
type_left, orig_type_left);
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
- return;
} else if (is_type_pointer(type_left) && is_type_integer(type_right)) {
check_pointer_arithmetic(&expression->base.source_position,
type_left, orig_type_left);
}
}
+static bool maybe_negative(expression_t const *const expr)
+{
+ return
+ !is_constant_expression(expr) ||
+ fold_constant(expr) < 0;
+}
+
/**
* Check the semantics of comparison expressions.
*
/* TODO non-arithmetic types */
if (is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
- /* test for signed vs unsigned compares */
- if (warning.sign_compare &&
- (expression->base.kind != EXPR_BINARY_EQUAL &&
- expression->base.kind != EXPR_BINARY_NOTEQUAL) &&
- (is_type_signed(type_left) != is_type_signed(type_right))) {
-
- /* check if 1 of the operands is a constant, in this case we just
- * check wether we can safely represent the resulting constant in
- * the type of the other operand. */
- expression_t *const_expr = NULL;
- expression_t *other_expr = NULL;
-
- if (is_constant_expression(left)) {
- const_expr = left;
- other_expr = right;
- } else if (is_constant_expression(right)) {
- const_expr = right;
- other_expr = left;
- }
+ type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
- if (const_expr != NULL) {
- type_t *other_type = skip_typeref(other_expr->base.type);
- long val = fold_constant(const_expr);
- /* TODO: check if val can be represented by other_type */
- (void) other_type;
- (void) val;
+ /* test for signed vs unsigned compares */
+ if (warning.sign_compare && is_type_integer(arithmetic_type)) {
+ bool const signed_left = is_type_signed(type_left);
+ bool const signed_right = is_type_signed(type_right);
+ if (signed_left != signed_right) {
+ /* FIXME long long needs better const folding magic */
+ /* TODO check whether constant value can be represented by other type */
+ if ((signed_left && maybe_negative(left)) ||
+ (signed_right && maybe_negative(right))) {
+ warningf(&expression->base.source_position,
+ "comparison between signed and unsigned");
+ }
}
- warningf(&expression->base.source_position,
- "comparison between signed and unsigned");
}
- type_t *arithmetic_type = semantic_arithmetic(type_left, type_right);
+
expression->left = create_implicit_cast(left, arithmetic_type);
expression->right = create_implicit_cast(right, arithmetic_type);
expression->base.type = arithmetic_type;
* @param precedence the precedence of the operator
*/
static void register_infix_parser(parse_expression_infix_function parser,
- int token_type, unsigned precedence)
+ int token_type, precedence_t precedence)
{
expression_parser_function_t *entry = &expression_parsers[token_type];
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;
}
if (c_mode & _MS) {
/* add predefined symbols for extended-decl-modifier */
- sym_align = symbol_table_insert("align");
- sym_allocate = symbol_table_insert("allocate");
- sym_dllimport = symbol_table_insert("dllimport");
- sym_dllexport = symbol_table_insert("dllexport");
- sym_naked = symbol_table_insert("naked");
- sym_noinline = symbol_table_insert("noinline");
- sym_noreturn = symbol_table_insert("noreturn");
- sym_nothrow = symbol_table_insert("nothrow");
- sym_novtable = symbol_table_insert("novtable");
- sym_property = symbol_table_insert("property");
- sym_get = symbol_table_insert("get");
- sym_put = symbol_table_insert("put");
- sym_selectany = symbol_table_insert("selectany");
- sym_thread = symbol_table_insert("thread");
- sym_uuid = symbol_table_insert("uuid");
- sym_deprecated = symbol_table_insert("deprecated");
- sym_restrict = symbol_table_insert("restrict");
- sym_noalias = symbol_table_insert("noalias");
+ sym_align = symbol_table_insert("align");
+ sym_allocate = symbol_table_insert("allocate");
+ sym_dllimport = symbol_table_insert("dllimport");
+ sym_dllexport = symbol_table_insert("dllexport");
+ sym_naked = symbol_table_insert("naked");
+ sym_noinline = symbol_table_insert("noinline");
+ sym_returns_twice = symbol_table_insert("returns_twice");
+ sym_noreturn = symbol_table_insert("noreturn");
+ sym_nothrow = symbol_table_insert("nothrow");
+ sym_novtable = symbol_table_insert("novtable");
+ sym_property = symbol_table_insert("property");
+ sym_get = symbol_table_insert("get");
+ sym_put = symbol_table_insert("put");
+ sym_selectany = symbol_table_insert("selectany");
+ sym_thread = symbol_table_insert("thread");
+ sym_uuid = symbol_table_insert("uuid");
+ sym_deprecated = symbol_table_insert("deprecated");
+ sym_restrict = symbol_table_insert("restrict");
+ sym_noalias = symbol_table_insert("noalias");
}
memset(token_anchor_set, 0, sizeof(token_anchor_set));