You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
#include "config.h"
#include "system.h"
parsing into this file; that will make implementing the new parser
much easier since it will be able to make use of these routines. */
+static tree do_poplevel (tree);
static tree maybe_convert_cond (tree);
static tree simplify_aggr_init_exprs_r (tree *, int *, void *);
static void emit_associated_thunks (tree);
/* Finish a scope. */
-tree
+static tree
do_poplevel (tree stmt_list)
{
tree block = NULL;
TREE_CHAIN (if_stmt) = NULL;
add_stmt (do_poplevel (scope));
finish_stmt ();
+ empty_body_warning (THEN_CLAUSE (if_stmt), ELSE_CLAUSE (if_stmt));
}
/* Begin a while-statement. Returns a newly created WHILE_STMT if
finish_return_stmt (tree expr)
{
tree r;
+ bool no_warning;
- expr = check_return_expr (expr);
+ expr = check_return_expr (expr, &no_warning);
if (!processing_template_decl)
{
if (DECL_DESTRUCTOR_P (current_function_decl)
}
r = build_stmt (RETURN_EXPR, expr);
+ TREE_NO_WARNING (r) |= no_warning;
r = maybe_cleanup_point_expr_void (r);
r = add_stmt (r);
finish_stmt ();
{
tree r;
tree t;
+ int ninputs = list_length (input_operands);
+ int noutputs = list_length (output_operands);
if (!processing_template_decl)
{
- int ninputs, noutputs;
const char *constraint;
const char **oconstraints;
bool allows_mem, allows_reg, is_inout;
tree operand;
int i;
- ninputs = list_length (input_operands);
- noutputs = list_length (output_operands);
oconstraints = (const char **) alloca (noutputs * sizeof (char *));
string = resolve_asm_operand_names (string, output_operands,
if (!lvalue_or_else (operand, lv_asm))
operand = error_mark_node;
+ if (operand != error_mark_node
+ && (TREE_READONLY (operand)
+ || CP_TYPE_CONST_P (TREE_TYPE (operand))
+ /* Functions are not modifiable, even though they are
+ lvalues. */
+ || TREE_CODE (TREE_TYPE (operand)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (operand)) == METHOD_TYPE
+ /* If it's an aggregate and any field is const, then it is
+ effectively const. */
+ || (CLASS_TYPE_P (TREE_TYPE (operand))
+ && C_TYPE_FIELDS_READONLY (TREE_TYPE (operand)))))
+ readonly_error (operand, "assignment (via 'asm' output)", 0);
+
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
oconstraints[i] = constraint;
r = build_stmt (ASM_EXPR, string,
output_operands, input_operands,
clobbers);
- ASM_VOLATILE_P (r) = volatile_p;
+ ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
r = maybe_cleanup_point_expr_void (r);
return add_stmt (r);
}
{
if (current_function_decl
&& DECL_STATIC_FUNCTION_P (current_function_decl))
- cp_error_at ("invalid use of member %qD in static member function",
- decl);
+ error ("invalid use of member %q+D in static member function", decl);
else
- cp_error_at ("invalid use of non-static data member %qD", decl);
+ error ("invalid use of non-static data member %q+D", decl);
error ("from this location");
return error_mark_node;
if (!access_type)
{
- cp_error_at ("object missing in reference to %qD", decl);
+ error ("object missing in reference to %q+D", decl);
error ("from this location");
return error_mark_node;
}
QUALIFYING_SCOPE is also non-null. Wrap this in a SCOPE_REF
for now. */
if (processing_template_decl)
- return build_min (SCOPE_REF, TREE_TYPE (decl),
- qualifying_scope, DECL_NAME (decl));
+ return build_qualified_name (TREE_TYPE (decl),
+ qualifying_scope,
+ DECL_NAME (decl),
+ /*template_p=*/false);
perform_or_defer_access_check (TYPE_BINFO (access_type), decl);
class named to the left of the "::" operator. DONE is true if this
expression is a complete postfix-expression; it is false if this
expression is followed by '->', '[', '(', etc. ADDRESS_P is true
- iff this expression is the operand of '&'. */
+ iff this expression is the operand of '&'. TEMPLATE_P is true iff
+ the qualified-id was of the form "A::template B". TEMPLATE_ARG_P
+ is true iff this qualified name appears as a template argument. */
tree
-finish_qualified_id_expr (tree qualifying_class, tree expr, bool done,
- bool address_p)
+finish_qualified_id_expr (tree qualifying_class,
+ tree expr,
+ bool done,
+ bool address_p,
+ bool template_p,
+ bool template_arg_p)
{
+ gcc_assert (TYPE_P (qualifying_class));
+
if (error_operand_p (expr))
return error_mark_node;
+ if (DECL_P (expr) || BASELINK_P (expr))
+ mark_used (expr);
+
+ if (template_p)
+ check_template_keyword (expr);
+
/* If EXPR occurs as the operand of '&', use special handling that
permits a pointer-to-member. */
if (address_p && done)
return expr;
}
- if (TREE_CODE (expr) == FIELD_DECL)
+ /* Within the scope of a class, turn references to non-static
+ members into expression of the form "this->...". */
+ if (template_arg_p)
+ /* But, within a template argument, we do not want make the
+ transformation, as there is no "this" pointer. */
+ ;
+ else if (TREE_CODE (expr) == FIELD_DECL)
expr = finish_non_static_data_member (expr, current_class_ref,
qualifying_class);
else if (BASELINK_P (expr) && !processing_template_decl)
}
/* Process the final expression of a statement expression. EXPR can be
- NULL, if the final expression is empty. Build up a TARGET_EXPR so
- that the result value can be safely returned to the enclosing
- expression. */
+ NULL, if the final expression is empty. Return a STATEMENT_LIST
+ containing all the statements in the statement-expression, or
+ ERROR_MARK_NODE if there was an error. */
tree
finish_stmt_expr_expr (tree expr, tree stmt_expr)
{
- tree result = NULL_TREE;
-
if (error_operand_p (expr))
return error_mark_node;
+ /* If the last statement does not have "void" type, then the value
+ of the last statement is the value of the entire expression. */
if (expr)
{
- if (!processing_template_decl && !VOID_TYPE_P (TREE_TYPE (expr)))
+ tree type;
+ type = TREE_TYPE (expr);
+ if (!dependent_type_p (type) && !VOID_TYPE_P (type))
{
- tree type = TREE_TYPE (expr);
-
- if (TREE_CODE (type) == ARRAY_TYPE
- || TREE_CODE (type) == FUNCTION_TYPE)
- expr = decay_conversion (expr);
-
- expr = require_complete_type (expr);
-
+ expr = decay_conversion (expr);
+ if (error_operand_p (expr))
+ return error_mark_node;
type = TREE_TYPE (expr);
-
- /* Build a TARGET_EXPR for this aggregate. finish_stmt_expr
- will then pull it apart so the lifetime of the target is
- within the scope of the expression containing this statement
- expression. */
- if (TREE_CODE (expr) == TARGET_EXPR)
- ;
- else if (!IS_AGGR_TYPE (type) || TYPE_HAS_TRIVIAL_INIT_REF (type))
- expr = build_target_expr_with_type (expr, type);
+ }
+ /* The type of the statement-expression is the type of the last
+ expression. */
+ TREE_TYPE (stmt_expr) = type;
+ /* We must take particular care if TYPE is a class type. In
+ particular if EXPR creates a temporary of class type, then it
+ must be destroyed at the semicolon terminating the last
+ statement -- but we must make a copy before that happens.
+
+ This problem is solved by using a TARGET_EXPR to initialize a
+ new temporary variable. The TARGET_EXPR itself is placed
+ outside the statement-expression. However, the last
+ statement in the statement-expression is transformed from
+ EXPR to (approximately) T = EXPR, where T is the new
+ temporary variable. Thus, the lifetime of the new temporary
+ extends to the full-expression surrounding the
+ statement-expression. */
+ if (!processing_template_decl && !VOID_TYPE_P (type))
+ {
+ tree target_expr;
+ if (CLASS_TYPE_P (type)
+ && !TYPE_HAS_TRIVIAL_INIT_REF (type))
+ {
+ target_expr = build_target_expr_with_type (expr, type);
+ expr = TARGET_EXPR_INITIAL (target_expr);
+ }
else
{
- /* Copy construct. */
- expr = build_special_member_call
- (NULL_TREE, complete_ctor_identifier,
- build_tree_list (NULL_TREE, expr),
- type, LOOKUP_NORMAL);
- expr = build_cplus_new (type, expr);
- gcc_assert (TREE_CODE (expr) == TARGET_EXPR);
+ /* Normally, build_target_expr will not create a
+ TARGET_EXPR for scalars. However, we need the
+ temporary here, in order to solve the scoping
+ problem described above. */
+ target_expr = force_target_expr (type, expr);
+ expr = TARGET_EXPR_INITIAL (target_expr);
+ expr = build2 (INIT_EXPR,
+ type,
+ TARGET_EXPR_SLOT (target_expr),
+ expr);
}
- }
-
- if (expr != error_mark_node)
- {
- result = build_stmt (EXPR_STMT, expr);
- EXPR_STMT_STMT_EXPR_RESULT (result) = 1;
- add_stmt (result);
+ TARGET_EXPR_INITIAL (target_expr) = NULL_TREE;
+ /* Save away the TARGET_EXPR in the TREE_TYPE field of the
+ STATEMENT_EXPR. We will retrieve it in
+ finish_stmt_expr. */
+ TREE_TYPE (stmt_expr) = target_expr;
}
}
- finish_stmt ();
+ /* Having modified EXPR to reflect the extra initialization, we now
+ treat it just like an ordinary statement. */
+ expr = finish_expr_stmt (expr);
- /* Remember the last expression so that finish_stmt_expr
- can pull it apart. */
- TREE_TYPE (stmt_expr) = result;
+ /* Mark the last statement so that we can recognize it as such at
+ template-instantiation time. */
+ if (expr && processing_template_decl)
+ EXPR_STMT_STMT_EXPR_RESULT (expr) = 1;
- return result;
+ return stmt_expr;
}
/* Finish a statement-expression. EXPR should be the value returned
tree
finish_stmt_expr (tree stmt_expr, bool has_no_scope)
{
- tree result, result_stmt, type;
- tree *result_stmt_p = NULL;
+ tree type;
+ tree result;
- result_stmt = TREE_TYPE (stmt_expr);
- TREE_TYPE (stmt_expr) = void_type_node;
- result = pop_stmt_list (stmt_expr);
+ if (error_operand_p (stmt_expr))
+ return error_mark_node;
- if (!result_stmt || VOID_TYPE_P (result_stmt))
- type = void_type_node;
- else
- {
- /* We need to search the statement expression for the result_stmt,
- since we'll need to replace it entirely. */
- tree t;
- result_stmt_p = &result;
- while (1)
- {
- t = *result_stmt_p;
- if (t == result_stmt)
- break;
+ gcc_assert (TREE_CODE (stmt_expr) == STATEMENT_LIST);
- switch (TREE_CODE (t))
- {
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i = tsi_last (t);
- result_stmt_p = tsi_stmt_ptr (i);
- break;
- }
- case BIND_EXPR:
- result_stmt_p = &BIND_EXPR_BODY (t);
- break;
- case TRY_FINALLY_EXPR:
- case TRY_CATCH_EXPR:
- case CLEANUP_STMT:
- result_stmt_p = &TREE_OPERAND (t, 0);
- break;
- default:
- gcc_unreachable ();
- }
- }
- type = TREE_TYPE (EXPR_STMT_EXPR (result_stmt));
- }
+ type = TREE_TYPE (stmt_expr);
+ result = pop_stmt_list (stmt_expr);
if (processing_template_decl)
{
TREE_SIDE_EFFECTS (result) = 1;
STMT_EXPR_NO_SCOPE (result) = has_no_scope;
}
- else if (!VOID_TYPE_P (type))
+ else if (!TYPE_P (type))
{
- /* Pull out the TARGET_EXPR that is the final expression. Put
- the target's init_expr as the final expression and then put
- the statement expression itself as the target's init
- expr. Finally, return the target expression. */
- tree init, target_expr = EXPR_STMT_EXPR (result_stmt);
- gcc_assert (TREE_CODE (target_expr) == TARGET_EXPR);
-
- /* The initializer will be void if the initialization is done by
- AGGR_INIT_EXPR; propagate that out to the statement-expression as
- a whole. */
- init = TREE_OPERAND (target_expr, 1);
- type = TREE_TYPE (init);
-
- init = maybe_cleanup_point_expr (init);
- *result_stmt_p = init;
-
- if (VOID_TYPE_P (type))
- /* No frobbing needed. */;
- else if (TREE_CODE (result) == BIND_EXPR)
- {
- /* The BIND_EXPR created in finish_compound_stmt is void; if we're
- returning a value directly, give it the appropriate type. */
- if (VOID_TYPE_P (TREE_TYPE (result)))
- TREE_TYPE (result) = type;
- else
- gcc_assert (same_type_p (TREE_TYPE (result), type));
- }
- else if (TREE_CODE (result) == STATEMENT_LIST)
- /* We need to wrap a STATEMENT_LIST in a BIND_EXPR so it can have a
- type other than void. FIXME why can't we just return a value
- from STATEMENT_LIST? */
- result = build3 (BIND_EXPR, type, NULL, result, NULL);
-
- TREE_OPERAND (target_expr, 1) = result;
- result = target_expr;
+ gcc_assert (TREE_CODE (type) == TARGET_EXPR);
+ TARGET_EXPR_INITIAL (type) = result;
+ TREE_TYPE (result) = void_type_node;
+ result = type;
}
return result;
/* The unqualified name could not be resolved. */
fn = unqualified_fn_lookup_error (identifier);
}
- else
- fn = identifier;
return fn;
}
if (!result)
/* A call to a namespace-scope function. */
- result = build_new_function_call (fn, args);
+ result = build_new_function_call (fn, args, koenig_p);
}
else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR)
{
the INITIALIZER_LIST is being cast. */
tree
-finish_compound_literal (tree type, tree initializer_list)
+finish_compound_literal (tree type, VEC(constructor_elt,gc) *initializer_list)
{
tree compound_literal;
/* Build a CONSTRUCTOR for the INITIALIZER_LIST. */
compound_literal = build_constructor (NULL_TREE, initializer_list);
- /* Mark it as a compound-literal. */
- TREE_HAS_CONSTRUCTOR (compound_literal) = 1;
if (processing_template_decl)
TREE_TYPE (compound_literal) = type;
else
{
/* Check the initialization. */
- compound_literal = digest_init (type, compound_literal, NULL);
+ compound_literal = reshape_init (type, compound_literal);
+ compound_literal = digest_init (type, compound_literal);
/* If the TYPE was an array type with an unknown bound, then we can
figure out the dimension now. For example, something like:
compound_literal, 1);
}
+ /* Mark it as a compound-literal. */
+ TREE_HAS_CONSTRUCTOR (compound_literal) = 1;
+
return compound_literal;
}
{
/* We also need to add this function to the
CLASSTYPE_METHOD_VEC. */
- add_method (current_class_type, decl, NULL_TREE);
-
- TREE_CHAIN (decl) = TYPE_METHODS (current_class_type);
- TYPE_METHODS (current_class_type) = decl;
+ if (add_method (current_class_type, decl, NULL_TREE))
+ {
+ TREE_CHAIN (decl) = TYPE_METHODS (current_class_type);
+ TYPE_METHODS (current_class_type) = decl;
- maybe_add_class_template_decl_list (current_class_type, decl,
- /*friend_p=*/0);
+ maybe_add_class_template_decl_list (current_class_type, decl,
+ /*friend_p=*/0);
+ }
}
/* Enter the DECL into the scope of the class. */
else if ((TREE_CODE (decl) == USING_DECL && !DECL_DEPENDENT_P (decl))
{
gcc_assert (pch_file);
- /* A non-template inline function with external linkage will always
- be COMDAT. As we must eventually determine the linkage of all
- functions, and as that causes writes to the data mapped in from
- the PCH file, it's advantageous to mark the functions at this
- point. */
- if (TREE_CODE (decl) == FUNCTION_DECL
- && TREE_PUBLIC (decl)
- && DECL_DECLARED_INLINE_P (decl)
- && !DECL_IMPLICIT_INSTANTIATION (decl))
- {
- comdat_linkage (decl);
- DECL_INTERFACE_KNOWN (decl) = 1;
- }
-
/* There's a good chance that we'll have to mangle names at some
point, even if only for emission in debugging information. */
if (TREE_CODE (decl) == VAR_DECL
void
qualified_name_lookup_error (tree scope, tree name, tree decl)
{
- if (TYPE_P (scope))
+ if (scope == error_mark_node)
+ ; /* We already complained. */
+ else if (TYPE_P (scope))
{
if (!COMPLETE_TYPE_P (scope))
error ("incomplete type %qT used in nested name specifier", scope);
constant-expression, but a non-constant expression is also
permissible.
+ DONE is true if this expression is a complete postfix-expression;
+ it is false if this expression is followed by '->', '[', '(', etc.
+ ADDRESS_P is true iff this expression is the operand of '&'.
+ TEMPLATE_P is true iff the qualified-id was of the form
+ "A::template B". TEMPLATE_ARG_P is true iff this qualified name
+ appears as a template argument.
+
If an error occurs, and it is the kind of error that might cause
the parser to abort a tentative parse, *ERROR_MSG is filled in. It
is the caller's responsibility to issue the message. *ERROR_MSG
tree decl,
tree scope,
cp_id_kind *idk,
- tree *qualifying_class,
bool integral_constant_expression_p,
bool allow_non_integral_constant_expression_p,
bool *non_integral_constant_expression_p,
+ bool template_p,
+ bool done,
+ bool address_p,
+ bool template_arg_p,
const char **error_msg)
{
/* Initialize the output parameters. */
was entirely defined. */
if (!scope && decl != error_mark_node)
maybe_note_name_used_in_class (id_expression, decl);
+
+ /* Disallow uses of local variables from containing functions. */
+ if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ {
+ tree context = decl_function_context (decl);
+ if (context != NULL_TREE && context != current_function_decl
+ && ! TREE_STATIC (decl))
+ {
+ error (TREE_CODE (decl) == VAR_DECL
+ ? "use of %<auto%> variable from containing function"
+ : "use of parameter from containing function");
+ error (" %q+#D declared here", decl);
+ return error_mark_node;
+ }
+ }
}
/* If we didn't find anything, or what we found was a type,
dependent. */
if (scope)
{
- if (TYPE_P (scope))
- *qualifying_class = scope;
/* Since this name was dependent, the expression isn't
constant -- yet. No error is issued because it might
be constant when things are instantiated. */
if (integral_constant_expression_p)
*non_integral_constant_expression_p = true;
- if (TYPE_P (scope) && dependent_type_p (scope))
- return build_nt (SCOPE_REF, scope, id_expression);
- else if (TYPE_P (scope) && DECL_P (decl))
- return convert_from_reference
- (build2 (SCOPE_REF, TREE_TYPE (decl), scope, id_expression));
- else
- return convert_from_reference (decl);
+ if (TYPE_P (scope))
+ {
+ if (address_p && done)
+ decl = finish_qualified_id_expr (scope, decl,
+ done, address_p,
+ template_p,
+ template_arg_p);
+ else if (dependent_type_p (scope))
+ decl = build_qualified_name (/*type=*/NULL_TREE,
+ scope,
+ id_expression,
+ template_p);
+ else if (DECL_P (decl))
+ decl = build_qualified_name (TREE_TYPE (decl),
+ scope,
+ id_expression,
+ template_p);
+ }
+ if (TREE_TYPE (decl))
+ decl = convert_from_reference (decl);
+ return decl;
}
/* A TEMPLATE_ID already contains all the information we
need. */
mark_used (decl);
if (TREE_CODE (decl) == FIELD_DECL || BASELINK_P (decl))
- *qualifying_class = scope;
+ decl = finish_qualified_id_expr (scope,
+ decl,
+ done,
+ address_p,
+ template_p,
+ template_arg_p);
else
{
tree r = convert_from_reference (decl);
- if (processing_template_decl
- && TYPE_P (scope))
- r = build2 (SCOPE_REF, TREE_TYPE (r), scope, decl);
+ if (processing_template_decl && TYPE_P (scope))
+ r = build_qualified_name (TREE_TYPE (r),
+ scope, decl,
+ template_p);
decl = r;
}
}
if (!really_overloaded_fn (decl))
mark_used (first_fn);
- if (TREE_CODE (first_fn) == FUNCTION_DECL
+ if (!template_arg_p
+ && TREE_CODE (first_fn) == FUNCTION_DECL
&& DECL_FUNCTION_MEMBER_P (first_fn)
&& !shared_member_p (decl))
{
/* A set of member functions. */
decl = maybe_dummy_object (DECL_CONTEXT (first_fn), 0);
- return finish_class_member_access_expr (decl, id_expression);
+ return finish_class_member_access_expr (decl, id_expression,
+ /*template_p=*/false);
}
}
else
{
- if (TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL)
- {
- tree context = decl_function_context (decl);
-
- if (context != NULL_TREE && context != current_function_decl
- && ! TREE_STATIC (decl))
- {
- error (TREE_CODE (decl) == VAR_DECL
- ? "use of %<auto%> variable from containing function"
- : "use of parameter from containing function");
- cp_error_at (" %q#D declared here", decl);
- return error_mark_node;
- }
- }
-
if (DECL_P (decl) && DECL_NONLOCAL (decl)
&& DECL_CLASS_SCOPE_P (decl)
&& DECL_CONTEXT (decl) != current_class_type)
decl = convert_from_reference (decl);
}
-
- /* Resolve references to variables of anonymous unions
- into COMPONENT_REFs. */
- if (TREE_CODE (decl) == ALIAS_DECL)
- decl = unshare_expr (DECL_INITIAL (decl));
}
if (TREE_DEPRECATED (decl))
style = arg;
}
- if (style == ctor || style == arg)
+ if (style == ctor)
{
- /* Pass the address of the slot. If this is a constructor, we
- replace the first argument; otherwise, we tack on a new one. */
+ /* Replace the first argument to the ctor with the address of the
+ slot. */
tree addr;
- if (style == ctor)
- args = TREE_CHAIN (args);
-
+ args = TREE_CHAIN (args);
cxx_mark_addressable (slot);
addr = build1 (ADDR_EXPR, build_pointer_type (type), slot);
- if (style == arg)
- {
- /* The return type might have different cv-quals from the slot. */
- tree fntype = TREE_TYPE (TREE_TYPE (fn));
-
- gcc_assert (TREE_CODE (fntype) == FUNCTION_TYPE
- || TREE_CODE (fntype) == METHOD_TYPE);
- addr = convert (build_pointer_type (TREE_TYPE (fntype)), addr);
- }
-
args = tree_cons (NULL_TREE, addr, args);
}
fn, args, NULL_TREE);
if (style == arg)
- /* Tell the backend that we've added our return slot to the argument
- list. */
- CALL_EXPR_HAS_RETURN_SLOT_ADDR (call_expr) = 1;
+ {
+ /* Just mark it addressable here, and leave the rest to
+ expand_call{,_inline}. */
+ cxx_mark_addressable (slot);
+ CALL_EXPR_RETURN_SLOT_OPT (call_expr) = true;
+ call_expr = build2 (MODIFY_EXPR, TREE_TYPE (call_expr), slot, call_expr);
+ }
else if (style == pcc)
{
/* If we're using the non-reentrant PCC calling convention, then we
call_expr = build_aggr_init (slot, call_expr,
DIRECT_BIND | LOOKUP_ONLYCONVERTING);
pop_deferring_access_checks ();
+ call_expr = build2 (COMPOUND_EXPR, TREE_TYPE (slot), call_expr, slot);
}
*tp = call_expr;
/* Normally, collection only occurs in rest_of_compilation. So,
if we don't collect here, we never collect junk generated
during the processing of templates until we hit a
- non-template function. */
- ggc_collect ();
+ non-template function. It's not safe to do this inside a
+ nested class, though, as the parser may have local state that
+ is not a GC root. */
+ if (!function_depth)
+ ggc_collect ();
return;
}
these functions so that it can inline them as appropriate. */
if (DECL_DECLARED_INLINE_P (fn) || DECL_IMPLICIT_INSTANTIATION (fn))
{
- if (!at_eof)
+ if (DECL_INTERFACE_KNOWN (fn))
+ /* We've already made a decision as to how this function will
+ be handled. */;
+ else if (!at_eof)
{
DECL_EXTERNAL (fn) = 1;
DECL_NOT_REALLY_EXTERN (fn) = 1;
note_vague_linkage_fn (fn);
+ /* A non-template inline function with external linkage will
+ always be COMDAT. As we must eventually determine the
+ linkage of all functions, and as that causes writes to
+ the data mapped in from the PCH file, it's advantageous
+ to mark the functions at this point. */
+ if (!DECL_IMPLICIT_INSTANTIATION (fn))
+ {
+ /* This function must have external linkage, as
+ otherwise DECL_INTERFACE_KNOWN would have been
+ set. */
+ gcc_assert (TREE_PUBLIC (fn));
+ comdat_linkage (fn);
+ DECL_INTERFACE_KNOWN (fn) = 1;
+ }
}
else
import_export_decl (fn);