static void initialize_local_var (tree, tree);
static void expand_static_init (tree, tree);
static location_t smallest_type_location (const cp_decl_specifier_seq*);
+static bool identify_goto (tree, location_t, const location_t *,
+ diagnostic_t, bool);
/* The following symbols are subsumed in the cp_global_trees array, and
listed here individually for documentation purposes.
or the inner scope popped. These are the decls that will *not* be
skipped when jumping to the label. */
tree names_in_scope;
+ /* If the use is a possible destination of a computed goto, a vec of decls
+ that aren't destroyed, filled in by poplevel_named_label_1. */
+ vec<tree,va_gc> *computed_goto;
/* The location of the goto, for error reporting. */
location_t o_goto_locus;
/* True if an OpenMP structured block scope has been closed since
/* A list of uses of the label, before the label is defined. */
named_label_use_entry *uses;
+ /* True if we've seen &&label. Appalently we can't use TREE_ADDRESSABLE for
+ this, it has a more specific meaning for LABEL_DECL. */
+ bool addressed;
+
/* The following bits are set after the label is defined, and are
updated as scopes are popped. They indicate that a jump to the
label will illegally enter a scope of the given flavor. */
for (use = ent->uses; use ; use = use->next)
if (use->binding_level == bl)
{
+ if (auto &cg = use->computed_goto)
+ for (tree d = use->names_in_scope; d; d = DECL_CHAIN (d))
+ if (TREE_CODE (d) == VAR_DECL && !TREE_STATIC (d)
+ && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (d)))
+ vec_safe_push (cg, d);
+
use->binding_level = obl;
use->names_in_scope = obl->names;
if (bl->kind == sk_omp)
return ent ? ent->label_decl : NULL_TREE;
}
+/* Remember that we've seen &&ID. */
+
+void
+mark_label_addressed (tree id)
+{
+ named_label_entry *ent = lookup_label_1 (id, false);
+ ent->addressed = true;
+}
+
tree
declare_local_label (tree id)
{
static bool
identify_goto (tree decl, location_t loc, const location_t *locus,
- diagnostic_t diag_kind)
+ diagnostic_t diag_kind, bool computed)
{
+ if (computed)
+ diag_kind = DK_WARNING;
bool complained
= emit_diagnostic (diag_kind, loc, 0,
decl ? G_("jump to label %qD")
: G_("jump to case label"), decl);
if (complained && locus)
- inform (*locus, " from here");
+ {
+ if (computed)
+ inform (*locus, " as a possible target of computed goto");
+ else
+ inform (*locus, " from here");
+ }
return complained;
}
/* Check that a single previously seen jump to a newly defined label
is OK. DECL is the LABEL_DECL or 0; LEVEL is the binding_level for
the jump context; NAMES are the names in scope in LEVEL at the jump
- context; LOCUS is the source position of the jump or 0. Returns
+ context; LOCUS is the source position of the jump or 0. COMPUTED
+ is a vec of decls if the jump is a computed goto. Returns
true if all is well. */
static bool
check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
- bool exited_omp, const location_t *locus)
+ bool exited_omp, const location_t *locus,
+ vec<tree,va_gc> *computed)
{
cp_binding_level *b;
bool complained = false;
if (exited_omp)
{
- complained = identify_goto (decl, input_location, locus, DK_ERROR);
+ complained = identify_goto (decl, input_location, locus, DK_ERROR,
+ computed);
if (complained)
inform (input_location, " exits OpenMP structured block");
saw_omp = true;
if (!identified)
{
- complained = identify_goto (decl, input_location, locus, DK_ERROR);
- identified = 1;
+ complained = identify_goto (decl, input_location, locus, DK_ERROR,
+ computed);
+ identified = 2;
}
if (complained)
inform (DECL_SOURCE_LOCATION (new_decls),
if (inf)
{
if (identified < 2)
- complained = identify_goto (decl, input_location, locus, DK_ERROR);
+ complained = identify_goto (decl, input_location, locus, DK_ERROR,
+ computed);
identified = 2;
if (complained)
inform (loc, inf);
}
}
+ if (!vec_safe_is_empty (computed))
+ {
+ if (!identified)
+ complained = identify_goto (decl, input_location, locus, DK_ERROR,
+ computed);
+ identified = 2;
+ if (complained)
+ for (tree d : computed)
+ inform (DECL_SOURCE_LOCATION (d), " does not destroy %qD", d);
+ }
+
return !identified;
}
{
check_previous_goto_1 (decl, use->binding_level,
use->names_in_scope, use->in_omp_scope,
- &use->o_goto_locus);
+ &use->o_goto_locus, use->computed_goto);
}
static bool
check_switch_goto (cp_binding_level* level)
{
- return check_previous_goto_1 (NULL_TREE, level, level->names, false, NULL);
+ return check_previous_goto_1 (NULL_TREE, level, level->names,
+ false, NULL, nullptr);
}
-/* Check that a new jump to a label DECL is OK. Called by
- finish_goto_stmt. */
+/* Check that a new jump to a label ENT is OK. COMPUTED is true
+ if this is a possible target of a computed goto. */
void
-check_goto (tree decl)
+check_goto_1 (named_label_entry *ent, bool computed)
{
- /* We can't know where a computed goto is jumping.
- So we assume that it's OK. */
- if (TREE_CODE (decl) != LABEL_DECL)
- return;
-
- hashval_t hash = IDENTIFIER_HASH_VALUE (DECL_NAME (decl));
- named_label_entry **slot
- = named_labels->find_slot_with_hash (DECL_NAME (decl), hash, NO_INSERT);
- named_label_entry *ent = *slot;
+ tree decl = ent->label_decl;
/* If the label hasn't been defined yet, defer checking. */
if (! DECL_INITIAL (decl))
new_use->names_in_scope = current_binding_level->names;
new_use->o_goto_locus = input_location;
new_use->in_omp_scope = false;
+ new_use->computed_goto = computed ? make_tree_vector () : nullptr;
new_use->next = ent->uses;
ent->uses = new_use;
|| ent->in_omp_scope || ent->in_stmt_expr)
diag_kind = DK_ERROR;
complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
- &input_location, diag_kind);
+ &input_location, diag_kind, computed);
identified = 1 + (diag_kind == DK_ERROR);
}
if (identified == 1)
{
complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
- &input_location, DK_ERROR);
+ &input_location, DK_ERROR, computed);
identified = 2;
}
if (complained)
{
complained = identify_goto (decl,
DECL_SOURCE_LOCATION (decl),
- &input_location, DK_ERROR);
+ &input_location, DK_ERROR,
+ computed);
identified = 2;
}
if (complained)
break;
}
}
+
+ /* Warn if a computed goto might involve a local variable going out of scope
+ without being cleaned up. */
+ if (computed)
+ {
+ auto level = ent->binding_level;
+ auto names = ent->names_in_scope;
+ for (auto b = current_binding_level; ; b = b->level_chain)
+ {
+ tree end = b == level ? names : NULL_TREE;
+ for (tree d = b->names; d != end; d = DECL_CHAIN (d))
+ {
+ if (TREE_CODE (d) == VAR_DECL && !TREE_STATIC (d)
+ && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (d)))
+ {
+ complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
+ &input_location, DK_ERROR,
+ computed);
+ if (complained)
+ inform (DECL_SOURCE_LOCATION (d),
+ " does not destroy %qD", d);
+ }
+ }
+ if (b == level)
+ break;
+ }
+ }
+}
+
+/* Check that a new jump to a label DECL is OK. Called by
+ finish_goto_stmt. */
+
+void
+check_goto (tree decl)
+{
+ if (!named_labels)
+ return;
+ if (TREE_CODE (decl) != LABEL_DECL)
+ {
+ /* We don't know where a computed goto is jumping,
+ so check all addressable labels. */
+ for (auto iter = named_labels->begin ();
+ iter != named_labels->end ();
+ ++iter)
+ {
+ auto ent = *iter;
+ if (ent->addressed)
+ check_goto_1 (ent, true);
+ }
+ }
+ else
+ {
+ hashval_t hash = IDENTIFIER_HASH_VALUE (DECL_NAME (decl));
+ named_label_entry **slot
+ = named_labels->find_slot_with_hash (DECL_NAME (decl), hash, NO_INSERT);
+ named_label_entry *ent = *slot;
+ check_goto_1 (ent, false);
+ }
}
/* Check that a return is ok wrt OpenMP structured blocks.