static stack_entry_t *environment_stack = NULL;
static stack_entry_t *label_stack = NULL;
static stack_entry_t *local_label_stack = NULL;
+/** The global file scope. */
static scope_t *global_scope = NULL;
+/** The current scope. */
static scope_t *scope = NULL;
static declaration_t *last_declaration = NULL;
+/** Point to the current function declaration if inside a function. */
static declaration_t *current_function = NULL;
static declaration_t *current_init_decl = NULL;
static switch_statement_t *current_switch = NULL;
next_token(); \
} while (0)
-static void set_scope(scope_t *new_scope)
+static void scope_push(scope_t *new_scope)
{
if (scope != NULL) {
scope->last_declaration = last_declaration;
+ new_scope->depth = scope->depth + 1;
}
- scope = new_scope;
+ new_scope->parent = scope;
+ scope = new_scope;
last_declaration = new_scope->last_declaration;
}
+static void scope_pop(void)
+{
+ scope->last_declaration = last_declaration;
+ scope = scope->parent;
+ last_declaration = scope->last_declaration;
+}
+
/**
* Search a symbol in a given namespace and returns its declaration or
* NULL if this symbol was not found.
namespace_t namespc = (namespace_t) declaration->namespc;
/* replace/add declaration into declaration list of the symbol */
- declaration_t *iter = symbol->declaration;
- if (iter == NULL) {
- symbol->declaration = declaration;
- } else {
- declaration_t *iter_last = NULL;
- for( ; iter != NULL; iter_last = iter, iter = iter->symbol_next) {
- /* replace an entry? */
- if (iter->namespc == namespc) {
- if (iter_last == NULL) {
- symbol->declaration = declaration;
- } else {
- iter_last->symbol_next = declaration;
- }
- declaration->symbol_next = iter->symbol_next;
- break;
- }
- }
- if (iter == NULL) {
- assert(iter_last->symbol_next == NULL);
- iter_last->symbol_next = declaration;
+ declaration_t **anchor;
+ declaration_t *iter;
+ for (anchor = &symbol->declaration;; anchor = &iter->symbol_next) {
+ iter = *anchor;
+ if (iter == NULL)
+ break;
+
+ /* replace an entry? */
+ if (iter->namespc == namespc) {
+ declaration->symbol_next = iter->symbol_next;
+ break;
}
}
+ *anchor = declaration;
/* remember old declaration */
stack_entry_t entry;
namespace_t namespc = (namespace_t)entry->namespc;
/* replace/remove declaration */
- declaration_t *declaration = symbol->declaration;
- assert(declaration != NULL);
- if (declaration->namespc == namespc) {
- if (old_declaration == NULL) {
- symbol->declaration = declaration->symbol_next;
- } else {
- symbol->declaration = old_declaration;
- }
- } else {
- declaration_t *iter_last = declaration;
- declaration_t *iter = declaration->symbol_next;
- for( ; iter != NULL; iter_last = iter, iter = iter->symbol_next) {
- /* replace an entry? */
- if (iter->namespc == namespc) {
- assert(iter_last != NULL);
- iter_last->symbol_next = old_declaration;
- if (old_declaration != NULL) {
- old_declaration->symbol_next = iter->symbol_next;
- }
- break;
- }
- }
+ declaration_t **anchor;
+ declaration_t *iter;
+ for (anchor = &symbol->declaration;; anchor = &iter->symbol_next) {
+ iter = *anchor;
assert(iter != NULL);
+ /* replace an entry? */
+ if (iter->namespc == namespc)
+ break;
}
+
+ /* Because of scopes and appending other namespaces to the end of
+ * the list, this must hold. */
+ assert((old_declaration != NULL ? old_declaration->symbol_next : NULL) == iter->symbol_next);
+ *anchor = old_declaration;
}
ARR_SHRINKLEN(*stack_ptr, (int) new_top);
}
}
-static declaration_t *parse_identifier_list(void)
+/**
+ * Parses an K&R identifier list and return a list of declarations.
+ *
+ * @param last points to the last declaration in the list
+ * @return the list of declarations
+ */
+static declaration_t *parse_identifier_list(declaration_t **last)
{
declaration_t *declarations = NULL;
declaration_t *last_declaration = NULL;
next_token();
} while (token.type == T_IDENTIFIER);
+ *last = last_declaration;
return declarations;
}
return declaration;
}
-static declaration_t *parse_parameters(function_type_t *type)
+/**
+ * Parses a function type parameter list and return a list of declarations.
+ *
+ * @param last point to the last element of the list
+ * @return the parameter list
+ */
+static declaration_t *parse_parameters(function_type_t *type, declaration_t **last)
{
declaration_t *declarations = NULL;
token_type_t la1_type = (token_type_t)look_ahead(1)->type;
if (la1_type == ',' || la1_type == ')') {
type->kr_style_parameters = true;
- declarations = parse_identifier_list();
+ declarations = parse_identifier_list(last);
goto parameters_finished;
}
}
expect(')');
restore_anchor_state(',', saved_comma_state);
+ *last = last_declaration;
return declarations;
end_error:
restore_anchor_state(',', saved_comma_state);
+ *last = NULL;
return NULL;
}
type = allocate_type_zero(TYPE_FUNCTION, HERE);
}
- declaration_t *parameters = parse_parameters(&type->function);
+ declaration_t *last;
+ declaration_t *parameters = parse_parameters(&type->function, &last);
if (declaration != NULL) {
- declaration->scope.declarations = parameters;
+ declaration->scope.declarations = parameters;
+ declaration->scope.last_declaration = last;
+ declaration->scope.is_parameter = true;
}
construct_function_type_t *construct_function_type =
}
assert(declaration != previous_declaration);
- if (previous_declaration != NULL
- && previous_declaration->parent_scope == scope) {
+ if (previous_declaration != NULL &&
+ previous_declaration->parent_scope->is_parameter &&
+ scope->depth == previous_declaration->parent_scope->depth + 1) {
+ errorf(&declaration->source_position,
+ "declaration '%#T' redeclares the parameter '%#T' (declared %P)",
+ orig_type, symbol, previous_declaration->type, symbol,
+ &previous_declaration->source_position);
+ goto finish;
+ }
+ if (previous_declaration != NULL &&
+ previous_declaration->parent_scope == scope) {
/* can happen for K&R style declarations */
if (previous_declaration->type == NULL) {
previous_declaration->type = declaration->type;
"no previous declaration for '%#T'", orig_type, symbol);
}
}
-
+finish:
assert(declaration->parent_scope == NULL);
assert(scope != NULL);
if (!type->function.kr_style_parameters)
return;
+ add_anchor_token('{');
+
/* push function parameters */
- int top = environment_top();
- scope_t *last_scope = scope;
- set_scope(&declaration->scope);
+ size_t const top = environment_top();
+ scope_push(&declaration->scope);
declaration_t *parameter = declaration->scope.declarations;
for ( ; parameter != NULL; parameter = parameter->next) {
/* pop function parameters */
assert(scope == &declaration->scope);
- set_scope(last_scope);
+ scope_pop();
environment_pop_to(top);
/* update function type */
}
declaration->type = type;
+
+ rem_anchor_token('{');
}
static bool first_err = true;
type = skip_typeref(declaration->type);
/* push function parameters and switch scope */
- int top = environment_top();
- scope_t *last_scope = scope;
- set_scope(&declaration->scope);
+ size_t const top = environment_top();
+ scope_push(&declaration->scope);
declaration_t *parameter = declaration->scope.declarations;
for( ; parameter != NULL; parameter = parameter->next) {
}
assert(scope == &declaration->scope);
- set_scope(last_scope);
+ scope_pop();
environment_pop_to(top);
}
}
}
- type_t *type = declaration->type;
+ type_t *orig_type = declaration->type;
/* we always do the auto-type conversions; the & and sizeof parser contains
* code to revert this! */
- type = automatic_type_conversion(type);
+ type_t *type = automatic_type_conversion(orig_type);
ref->declaration = declaration;
ref->base.type = type;
/* this declaration is used */
declaration->used = true;
+ if (declaration->parent_scope != global_scope &&
+ declaration->parent_scope->depth < current_function->scope.depth &&
+ is_type_valid(orig_type) && !is_type_function(orig_type)) {
+ /* access of a variable from an outer function */
+ declaration->address_taken = true;
+ ref->is_outer_ref = true;
+ current_function->need_closure = true;
+ }
+
/* check for deprecated functions */
if (warning.deprecated_declarations &&
declaration->modifiers & DM_DEPRECATED) {
candidate = get_declaration(symbol, NAMESPACE_LOCAL_LABEL);
/* if we found a local label, we already created the declaration */
if (candidate != NULL) {
- assert(candidate->parent_scope == scope);
+ if (candidate->parent_scope != scope) {
+ assert(candidate->parent_scope->depth < scope->depth);
+ current_function->goto_to_outer = true;
+ }
return candidate;
}
"traditional C rejects the unary plus operator");
}
+static expression_t const *get_reference_address(expression_t const *expr)
+{
+ bool regular_take_address = true;
+ for (;;) {
+ if (expr->kind == EXPR_UNARY_TAKE_ADDRESS) {
+ expr = expr->unary.value;
+ } else {
+ regular_take_address = false;
+ }
+
+ if (expr->kind != EXPR_UNARY_DEREFERENCE)
+ break;
+
+ expr = expr->unary.value;
+ }
+
+ if (expr->kind != EXPR_REFERENCE)
+ return NULL;
+
+ if (!regular_take_address &&
+ !is_type_function(skip_typeref(expr->reference.declaration->type))) {
+ return NULL;
+ }
+
+ return expr;
+}
+
+static void warn_function_address_as_bool(expression_t const* expr)
+{
+ if (!warning.address)
+ return;
+
+ expr = get_reference_address(expr);
+ if (expr != NULL) {
+ warningf(&expr->base.source_position,
+ "the address of '%Y' will always evaluate as 'true'",
+ expr->reference.declaration->symbol);
+ }
+}
+
static void semantic_not(unary_expression_t *expression)
{
type_t *const orig_type = expression->value->base.type;
"operand of ! must be of scalar type");
}
+ warn_function_address_as_bool(expression->value);
+
expression->base.type = type_int;
}
}
}
+static void warn_string_literal_address(expression_t const* expr)
+{
+ while (expr->kind == EXPR_UNARY_TAKE_ADDRESS) {
+ expr = expr->unary.value;
+ if (expr->kind != EXPR_UNARY_DEREFERENCE)
+ return;
+ expr = expr->unary.value;
+ }
+
+ if (expr->kind == EXPR_STRING_LITERAL ||
+ expr->kind == EXPR_WIDE_STRING_LITERAL) {
+ warningf(&expr->base.source_position,
+ "comparison with string literal results in unspecified behaviour");
+ }
+}
+
/**
* Check the semantics of comparison expressions.
*
*/
static void semantic_comparison(binary_expression_t *expression)
{
- expression_t *left = expression->left;
- expression_t *right = expression->right;
- type_t *orig_type_left = left->base.type;
- type_t *orig_type_right = right->base.type;
+ expression_t *left = expression->left;
+ expression_t *right = expression->right;
- type_t *type_left = skip_typeref(orig_type_left);
- type_t *type_right = skip_typeref(orig_type_right);
+ if (warning.address) {
+ warn_string_literal_address(left);
+ warn_string_literal_address(right);
+
+ expression_t const* const func_left = get_reference_address(left);
+ if (func_left != NULL && is_null_pointer_constant(right)) {
+ warningf(&expression->base.source_position,
+ "the address of '%Y' will never be NULL",
+ func_left->reference.declaration->symbol);
+ }
+
+ expression_t const* const func_right = get_reference_address(right);
+ if (func_right != NULL && is_null_pointer_constant(right)) {
+ warningf(&expression->base.source_position,
+ "the address of '%Y' will never be NULL",
+ func_right->reference.declaration->symbol);
+ }
+ }
+
+ type_t *orig_type_left = left->base.type;
+ type_t *orig_type_right = right->base.type;
+ type_t *type_left = skip_typeref(orig_type_left);
+ type_t *type_right = skip_typeref(orig_type_right);
/* TODO non-arithmetic types */
if (is_type_arithmetic(type_left) && is_type_arithmetic(type_right)) {
type_t *const type_left = skip_typeref(orig_type_left);
type_t *const type_right = skip_typeref(orig_type_right);
+ warn_function_address_as_bool(left);
+ warn_function_address_as_bool(right);
+
if (!is_type_scalar(type_left) || !is_type_scalar(type_right)) {
/* TODO: improve error message */
if (is_type_valid(type_left) && is_type_valid(type_right)) {
PUSH_PARENT(statement);
- int top = environment_top();
- scope_t *last_scope = scope;
- set_scope(&statement->fors.scope);
+ size_t const top = environment_top();
+ scope_push(&statement->fors.scope);
expect('(');
add_anchor_token(')');
statement->fors.body = parse_loop_body(statement);
assert(scope == &statement->fors.scope);
- set_scope(last_scope);
+ scope_pop();
environment_pop_to(top);
POP_PARENT;
POP_PARENT;
rem_anchor_token(')');
assert(scope == &statement->fors.scope);
- set_scope(last_scope);
+ scope_pop();
environment_pop_to(top);
return create_invalid_statement();
statement = allocate_statement_zero(STATEMENT_GOTO);
statement->base.source_position = source_position;
statement->gotos.label = get_label(symbol);
+
+ if (statement->gotos.label->parent_scope->depth < current_function->scope.depth) {
+ statement->gotos.outer_fkt_jmp = true;
+ }
}
/* remember the goto's in a list for later checking */
eat('{');
add_anchor_token('}');
- int top = environment_top();
- int top_local = local_label_top();
- scope_t *last_scope = scope;
- set_scope(&statement->compound.scope);
+ size_t const top = environment_top();
+ size_t const top_local = local_label_top();
+ scope_push(&statement->compound.scope);
statement_t **anchor = &statement->compound.statements;
bool only_decls_so_far = true;
end_error:
rem_anchor_token('}');
assert(scope == &statement->compound.scope);
- set_scope(last_scope);
+ scope_pop();
environment_pop_to(top);
local_label_pop_to(top_local);
global_scope = &unit->scope;
assert(scope == NULL);
- set_scope(&unit->scope);
+ scope_push(&unit->scope);
initialize_builtin_types();
}
translation_unit_t *finish_parsing(void)
{
+ /* do NOT use scope_pop() here, this will crash, will it by hand */
assert(scope == &unit->scope);
- scope = NULL;
+ scope = NULL;
last_declaration = NULL;
assert(global_scope == &unit->scope);