1227 lines
36 KiB
C
1227 lines
36 KiB
C
/* Subroutines shared by all languages that are variants of C.
|
||
Copyright (C) 1992 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU CC.
|
||
|
||
GNU CC is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
GNU CC is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU CC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||
|
||
#ifndef lint
|
||
static char rcsid[] = "$Id: c-common.c,v 1.2 1993/08/02 17:33:30 mycroft Exp $";
|
||
#endif /* not lint */
|
||
|
||
#include "config.h"
|
||
#include "tree.h"
|
||
#include "c-lex.h"
|
||
#include "c-tree.h"
|
||
#include "flags.h"
|
||
#include "obstack.h"
|
||
#include <stdio.h>
|
||
|
||
extern struct obstack permanent_obstack;
|
||
|
||
/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
|
||
|
||
void
|
||
declare_function_name ()
|
||
{
|
||
tree decl, type, init;
|
||
char *name, *printable_name;
|
||
int len;
|
||
|
||
if (current_function_decl == NULL)
|
||
{
|
||
name = "";
|
||
printable_name = "top level";
|
||
}
|
||
else
|
||
{
|
||
char *kind = "function";
|
||
if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
|
||
kind = "method";
|
||
/* Allow functions to be nameless (such as artificial ones). */
|
||
if (DECL_NAME (current_function_decl))
|
||
name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
|
||
else
|
||
name = "";
|
||
printable_name = (*decl_printable_name) (current_function_decl, &kind);
|
||
}
|
||
|
||
/* If the default size of char arrays isn't big enough for the name,
|
||
make a bigger one. */
|
||
len = strlen (name) + 1;
|
||
type = char_array_type_node;
|
||
if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
|
||
< len)
|
||
type = build_array_type (char_type_node,
|
||
build_index_type (build_int_2 (len, 0)));
|
||
|
||
push_obstacks_nochange ();
|
||
decl = build_decl (VAR_DECL, get_identifier ("__FUNCTION__"), type);
|
||
TREE_STATIC (decl) = 1;
|
||
TREE_READONLY (decl) = 1;
|
||
DECL_SOURCE_LINE (decl) = 0;
|
||
DECL_IN_SYSTEM_HEADER (decl) = 1;
|
||
DECL_IGNORED_P (decl) = 1;
|
||
init = build_string (len, name);
|
||
TREE_TYPE (init) = type;
|
||
DECL_INITIAL (decl) = init;
|
||
finish_decl (pushdecl (decl), init, NULL_TREE);
|
||
|
||
len = strlen (printable_name) + 1;
|
||
type = char_array_type_node;
|
||
if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
|
||
< len)
|
||
type = build_array_type (char_type_node,
|
||
build_index_type (build_int_2 (len, 0)));
|
||
|
||
push_obstacks_nochange ();
|
||
decl = build_decl (VAR_DECL, get_identifier ("__PRETTY_FUNCTION__"), type);
|
||
TREE_STATIC (decl) = 1;
|
||
TREE_READONLY (decl) = 1;
|
||
DECL_SOURCE_LINE (decl) = 0;
|
||
DECL_IN_SYSTEM_HEADER (decl) = 1;
|
||
DECL_IGNORED_P (decl) = 1;
|
||
init = build_string (len, printable_name);
|
||
TREE_TYPE (init) = type;
|
||
DECL_INITIAL (decl) = init;
|
||
finish_decl (pushdecl (decl), init, NULL_TREE);
|
||
}
|
||
|
||
/* Given a chain of STRING_CST nodes,
|
||
concatenate them into one STRING_CST
|
||
and give it a suitable array-of-chars data type. */
|
||
|
||
tree
|
||
combine_strings (strings)
|
||
tree strings;
|
||
{
|
||
register tree value, t;
|
||
register int length = 1;
|
||
int wide_length = 0;
|
||
int wide_flag = 0;
|
||
int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
|
||
int nchars;
|
||
|
||
if (TREE_CHAIN (strings))
|
||
{
|
||
/* More than one in the chain, so concatenate. */
|
||
register char *p, *q;
|
||
|
||
/* Don't include the \0 at the end of each substring,
|
||
except for the last one.
|
||
Count wide strings and ordinary strings separately. */
|
||
for (t = strings; t; t = TREE_CHAIN (t))
|
||
{
|
||
if (TREE_TYPE (t) == wchar_array_type_node)
|
||
{
|
||
wide_length += (TREE_STRING_LENGTH (t) - wchar_bytes);
|
||
wide_flag = 1;
|
||
}
|
||
else
|
||
length += (TREE_STRING_LENGTH (t) - 1);
|
||
}
|
||
|
||
/* If anything is wide, the non-wides will be converted,
|
||
which makes them take more space. */
|
||
if (wide_flag)
|
||
length = length * wchar_bytes + wide_length;
|
||
|
||
p = savealloc (length);
|
||
|
||
/* Copy the individual strings into the new combined string.
|
||
If the combined string is wide, convert the chars to ints
|
||
for any individual strings that are not wide. */
|
||
|
||
q = p;
|
||
for (t = strings; t; t = TREE_CHAIN (t))
|
||
{
|
||
int len = (TREE_STRING_LENGTH (t)
|
||
- ((TREE_TYPE (t) == wchar_array_type_node)
|
||
? wchar_bytes : 1));
|
||
if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag)
|
||
{
|
||
bcopy (TREE_STRING_POINTER (t), q, len);
|
||
q += len;
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
for (i = 0; i < len; i++)
|
||
((int *) q)[i] = TREE_STRING_POINTER (t)[i];
|
||
q += len * wchar_bytes;
|
||
}
|
||
}
|
||
if (wide_flag)
|
||
{
|
||
int i;
|
||
for (i = 0; i < wchar_bytes; i++)
|
||
*q++ = 0;
|
||
}
|
||
else
|
||
*q = 0;
|
||
|
||
value = make_node (STRING_CST);
|
||
TREE_STRING_POINTER (value) = p;
|
||
TREE_STRING_LENGTH (value) = length;
|
||
TREE_CONSTANT (value) = 1;
|
||
}
|
||
else
|
||
{
|
||
value = strings;
|
||
length = TREE_STRING_LENGTH (value);
|
||
if (TREE_TYPE (value) == wchar_array_type_node)
|
||
wide_flag = 1;
|
||
}
|
||
|
||
/* Compute the number of elements, for the array type. */
|
||
nchars = wide_flag ? length / wchar_bytes : length;
|
||
|
||
/* Create the array type for the string constant.
|
||
-Wwrite-strings says make the string constant an array of const char
|
||
so that copying it to a non-const pointer will get a warning. */
|
||
if (warn_write_strings
|
||
&& (! flag_traditional && ! flag_writable_strings))
|
||
{
|
||
tree elements
|
||
= build_type_variant (wide_flag ? wchar_type_node : char_type_node,
|
||
1, 0);
|
||
TREE_TYPE (value)
|
||
= build_array_type (elements,
|
||
build_index_type (build_int_2 (nchars - 1, 0)));
|
||
}
|
||
else
|
||
TREE_TYPE (value)
|
||
= build_array_type (wide_flag ? wchar_type_node : char_type_node,
|
||
build_index_type (build_int_2 (nchars - 1, 0)));
|
||
TREE_CONSTANT (value) = 1;
|
||
TREE_STATIC (value) = 1;
|
||
return value;
|
||
}
|
||
|
||
/* Process the attributes listed in ATTRIBUTES
|
||
and install them in DECL. */
|
||
|
||
void
|
||
decl_attributes (decl, attributes)
|
||
tree decl, attributes;
|
||
{
|
||
tree a;
|
||
for (a = attributes; a; a = TREE_CHAIN (a))
|
||
if (TREE_VALUE (a) == get_identifier ("packed"))
|
||
{
|
||
if (TREE_CODE (decl) == FIELD_DECL)
|
||
DECL_PACKED (decl) = 1;
|
||
/* We can't set DECL_PACKED for a VAR_DECL, because the bit is
|
||
used for DECL_REGISTER. It wouldn't mean anything anyway. */
|
||
}
|
||
else if (TREE_VALUE (a) != 0
|
||
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
||
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode"))
|
||
{
|
||
int i;
|
||
char *specified_name
|
||
= IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (a)));
|
||
|
||
/* Give this decl a type with the specified mode. */
|
||
for (i = 0; i < NUM_MACHINE_MODES; i++)
|
||
if (!strcmp (specified_name, GET_MODE_NAME (i)))
|
||
{
|
||
tree type
|
||
= type_for_mode (i, TREE_UNSIGNED (TREE_TYPE (decl)));
|
||
if (type != 0)
|
||
{
|
||
TREE_TYPE (decl) = type;
|
||
DECL_SIZE (decl) = 0;
|
||
layout_decl (decl, 0);
|
||
}
|
||
else
|
||
error ("no data type for mode `%s'", specified_name);
|
||
break;
|
||
}
|
||
if (i == NUM_MACHINE_MODES)
|
||
error ("unknown machine mode `%s'", specified_name);
|
||
}
|
||
else if (TREE_VALUE (a) != 0
|
||
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
||
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned"))
|
||
{
|
||
int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a)))
|
||
* BITS_PER_UNIT;
|
||
|
||
if (exact_log2 (align) == -1)
|
||
error_with_decl (decl,
|
||
"requested alignment of `%s' is not a power of 2");
|
||
else if (TREE_CODE (decl) != VAR_DECL
|
||
&& TREE_CODE (decl) != FIELD_DECL)
|
||
error_with_decl (decl,
|
||
"alignment specified for `%s'");
|
||
else
|
||
DECL_ALIGN (decl) = align;
|
||
}
|
||
else if (TREE_VALUE (a) != 0
|
||
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
||
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("format"))
|
||
{
|
||
tree list = TREE_VALUE (TREE_VALUE (a));
|
||
tree format_type = TREE_PURPOSE (list);
|
||
int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list)));
|
||
int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
|
||
int is_scan;
|
||
tree argument;
|
||
int arg_num;
|
||
|
||
if (TREE_CODE (decl) != FUNCTION_DECL)
|
||
{
|
||
error_with_decl (decl,
|
||
"argument format specified for non-function `%s'");
|
||
return;
|
||
}
|
||
|
||
if (format_type == get_identifier ("printf"))
|
||
is_scan = 0;
|
||
else if (format_type == get_identifier ("scanf"))
|
||
is_scan = 1;
|
||
else
|
||
{
|
||
error_with_decl (decl, "unrecognized format specifier for `%s'");
|
||
return;
|
||
}
|
||
|
||
if (first_arg_num != 0 && first_arg_num <= format_num)
|
||
{
|
||
error_with_decl (decl,
|
||
"format string arg follows the args to be formatted, for `%s'");
|
||
return;
|
||
}
|
||
|
||
/* Verify that the format_num argument is actually a string, in case
|
||
the format attribute is in error. */
|
||
argument = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
||
for (arg_num = 1; ; ++arg_num)
|
||
{
|
||
if (argument == 0 || arg_num == format_num)
|
||
break;
|
||
argument = TREE_CHAIN (argument);
|
||
}
|
||
if (! argument
|
||
|| TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
|
||
|| (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
|
||
!= char_type_node))
|
||
{
|
||
error_with_decl (decl,
|
||
"format string arg not a string type, for `%s'");
|
||
return;
|
||
}
|
||
if (first_arg_num != 0)
|
||
{
|
||
/* Verify that first_arg_num points to the last arg, the ... */
|
||
while (argument)
|
||
arg_num++, argument = TREE_CHAIN (argument);
|
||
if (arg_num != first_arg_num)
|
||
{
|
||
error_with_decl (decl,
|
||
"args to be formatted is not ..., for `%s'");
|
||
return;
|
||
}
|
||
}
|
||
|
||
record_format_info (DECL_NAME (decl), is_scan, format_num,
|
||
first_arg_num);
|
||
}
|
||
}
|
||
|
||
/* Print a warning if a constant expression had overflow in folding.
|
||
Invoke this function on every expression that the language
|
||
requires to be a constant expression.
|
||
Note the ANSI C standard says it is erroneous for a
|
||
constant expression to overflow. */
|
||
|
||
void
|
||
constant_expression_warning (value)
|
||
tree value;
|
||
{
|
||
if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
|
||
{
|
||
/* ??? This is a warning, not a pedwarn, in 2.4,
|
||
because it happens in contexts that are not
|
||
"constant expressions" in ANSI C.
|
||
Fix the problem differently in 2.5. */
|
||
warning ("overflow in constant expression");
|
||
/* Suppress duplicate warnings. */
|
||
TREE_CONSTANT_OVERFLOW (value) = 0;
|
||
}
|
||
}
|
||
|
||
/* Print a warning if an expression had overflow in folding.
|
||
Invoke this function on every expression that
|
||
(1) appears in the source code, and
|
||
(2) might be a constant expression that overflowed, and
|
||
(3) is not already checked by convert_and_check;
|
||
however, do not invoke this function on operands of explicit casts. */
|
||
|
||
void
|
||
overflow_warning (value)
|
||
tree value;
|
||
{
|
||
if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
|
||
{
|
||
/* ??? This is a warning, not a pedwarn, in 2.4,
|
||
because it happens in contexts that are not
|
||
"constant expressions" in ANSI C.
|
||
Fix the problem differently in 2.5. */
|
||
warning ("integer overflow in expression");
|
||
TREE_CONSTANT_OVERFLOW (value) = 0;
|
||
}
|
||
}
|
||
|
||
/* Print a warning if a large constant is truncated to unsigned,
|
||
or if -Wconversion is used and a constant < 0 is converted to unsigned.
|
||
Invoke this function on every expression that might be implicitly
|
||
converted to an unsigned type. */
|
||
|
||
void
|
||
unsigned_conversion_warning (result, operand)
|
||
tree result, operand;
|
||
{
|
||
if (TREE_CODE (operand) == INTEGER_CST
|
||
&& TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
|
||
&& TREE_UNSIGNED (TREE_TYPE (result))
|
||
&& !int_fits_type_p (operand, TREE_TYPE (result)))
|
||
{
|
||
if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
|
||
/* This detects cases like converting -129 or 256 to unsigned char. */
|
||
pedwarn ("large integer implicitly truncated to unsigned type");
|
||
else if (warn_conversion)
|
||
pedwarn ("negative integer implicitly converted to unsigned type");
|
||
}
|
||
}
|
||
|
||
/* Convert EXPR to TYPE, warning about conversion problems with constants.
|
||
Invoke this function on every expression that is converted implicitly,
|
||
i.e. because of language rules and not because of an explicit cast. */
|
||
|
||
tree
|
||
convert_and_check (type, expr)
|
||
tree type, expr;
|
||
{
|
||
tree t = convert (type, expr);
|
||
if (TREE_CODE (t) == INTEGER_CST)
|
||
{
|
||
if (TREE_UNSIGNED (TREE_TYPE (expr))
|
||
&& !TREE_UNSIGNED (type)
|
||
&& TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
|
||
&& TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))
|
||
/* No warning for converting 0x80000000 to int. */
|
||
TREE_CONSTANT_OVERFLOW (t) = 0;
|
||
else if (TREE_CONSTANT_OVERFLOW (t))
|
||
{
|
||
/* ??? This is a warning, not a pedwarn, in 2.4,
|
||
because it happens in contexts that are not
|
||
"constant expressions" in ANSI C.
|
||
Fix the problem differently in 2.5. */
|
||
warning ("overflow in implicit constant conversion");
|
||
TREE_CONSTANT_OVERFLOW (t) = 0;
|
||
}
|
||
else
|
||
unsigned_conversion_warning (t, expr);
|
||
}
|
||
return t;
|
||
}
|
||
|
||
void
|
||
c_expand_expr_stmt (expr)
|
||
tree expr;
|
||
{
|
||
/* Do default conversion if safe and possibly important,
|
||
in case within ({...}). */
|
||
if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr))
|
||
|| TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
|
||
expr = default_conversion (expr);
|
||
|
||
if (TREE_TYPE (expr) != error_mark_node
|
||
&& TYPE_SIZE (TREE_TYPE (expr)) == 0
|
||
&& TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
|
||
error ("expression statement has incomplete type");
|
||
|
||
expand_expr_stmt (expr);
|
||
}
|
||
|
||
/* Validate the expression after `case' and apply default promotions. */
|
||
|
||
tree
|
||
check_case_value (value)
|
||
tree value;
|
||
{
|
||
if (value == NULL_TREE)
|
||
return value;
|
||
|
||
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
|
||
STRIP_TYPE_NOPS (value);
|
||
|
||
if (TREE_CODE (value) != INTEGER_CST
|
||
&& value != error_mark_node)
|
||
{
|
||
error ("case label does not reduce to an integer constant");
|
||
value = error_mark_node;
|
||
}
|
||
else
|
||
/* Promote char or short to int. */
|
||
value = default_conversion (value);
|
||
|
||
constant_expression_warning (value);
|
||
|
||
return value;
|
||
}
|
||
|
||
/* Return an integer type with BITS bits of precision,
|
||
that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */
|
||
|
||
tree
|
||
type_for_size (bits, unsignedp)
|
||
unsigned bits;
|
||
int unsignedp;
|
||
{
|
||
if (bits == TYPE_PRECISION (signed_char_type_node))
|
||
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
||
|
||
if (bits == TYPE_PRECISION (short_integer_type_node))
|
||
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
||
|
||
if (bits == TYPE_PRECISION (integer_type_node))
|
||
return unsignedp ? unsigned_type_node : integer_type_node;
|
||
|
||
if (bits == TYPE_PRECISION (long_integer_type_node))
|
||
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
||
|
||
if (bits == TYPE_PRECISION (long_long_integer_type_node))
|
||
return (unsignedp ? long_long_unsigned_type_node
|
||
: long_long_integer_type_node);
|
||
|
||
if (bits <= TYPE_PRECISION (intQI_type_node))
|
||
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
|
||
|
||
if (bits <= TYPE_PRECISION (intHI_type_node))
|
||
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
|
||
|
||
if (bits <= TYPE_PRECISION (intSI_type_node))
|
||
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
|
||
|
||
if (bits <= TYPE_PRECISION (intDI_type_node))
|
||
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Return a data type that has machine mode MODE.
|
||
If the mode is an integer,
|
||
then UNSIGNEDP selects between signed and unsigned types. */
|
||
|
||
tree
|
||
type_for_mode (mode, unsignedp)
|
||
enum machine_mode mode;
|
||
int unsignedp;
|
||
{
|
||
if (mode == TYPE_MODE (signed_char_type_node))
|
||
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
||
|
||
if (mode == TYPE_MODE (short_integer_type_node))
|
||
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
||
|
||
if (mode == TYPE_MODE (integer_type_node))
|
||
return unsignedp ? unsigned_type_node : integer_type_node;
|
||
|
||
if (mode == TYPE_MODE (long_integer_type_node))
|
||
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
||
|
||
if (mode == TYPE_MODE (long_long_integer_type_node))
|
||
return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node;
|
||
|
||
if (mode == TYPE_MODE (intQI_type_node))
|
||
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
|
||
|
||
if (mode == TYPE_MODE (intHI_type_node))
|
||
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
|
||
|
||
if (mode == TYPE_MODE (intSI_type_node))
|
||
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
|
||
|
||
if (mode == TYPE_MODE (intDI_type_node))
|
||
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
|
||
|
||
if (mode == TYPE_MODE (float_type_node))
|
||
return float_type_node;
|
||
|
||
if (mode == TYPE_MODE (double_type_node))
|
||
return double_type_node;
|
||
|
||
if (mode == TYPE_MODE (long_double_type_node))
|
||
return long_double_type_node;
|
||
|
||
if (mode == TYPE_MODE (build_pointer_type (char_type_node)))
|
||
return build_pointer_type (char_type_node);
|
||
|
||
if (mode == TYPE_MODE (build_pointer_type (integer_type_node)))
|
||
return build_pointer_type (integer_type_node);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Print an error message for invalid operands to arith operation CODE.
|
||
NOP_EXPR is used as a special case (see truthvalue_conversion). */
|
||
|
||
void
|
||
binary_op_error (code)
|
||
enum tree_code code;
|
||
{
|
||
register char *opname;
|
||
switch (code)
|
||
{
|
||
case NOP_EXPR:
|
||
error ("invalid truth-value expression");
|
||
return;
|
||
|
||
case PLUS_EXPR:
|
||
opname = "+"; break;
|
||
case MINUS_EXPR:
|
||
opname = "-"; break;
|
||
case MULT_EXPR:
|
||
opname = "*"; break;
|
||
case MAX_EXPR:
|
||
opname = "max"; break;
|
||
case MIN_EXPR:
|
||
opname = "min"; break;
|
||
case EQ_EXPR:
|
||
opname = "=="; break;
|
||
case NE_EXPR:
|
||
opname = "!="; break;
|
||
case LE_EXPR:
|
||
opname = "<="; break;
|
||
case GE_EXPR:
|
||
opname = ">="; break;
|
||
case LT_EXPR:
|
||
opname = "<"; break;
|
||
case GT_EXPR:
|
||
opname = ">"; break;
|
||
case LSHIFT_EXPR:
|
||
opname = "<<"; break;
|
||
case RSHIFT_EXPR:
|
||
opname = ">>"; break;
|
||
case TRUNC_MOD_EXPR:
|
||
case FLOOR_MOD_EXPR:
|
||
opname = "%"; break;
|
||
case TRUNC_DIV_EXPR:
|
||
case FLOOR_DIV_EXPR:
|
||
opname = "/"; break;
|
||
case BIT_AND_EXPR:
|
||
opname = "&"; break;
|
||
case BIT_IOR_EXPR:
|
||
opname = "|"; break;
|
||
case TRUTH_ANDIF_EXPR:
|
||
opname = "&&"; break;
|
||
case TRUTH_ORIF_EXPR:
|
||
opname = "||"; break;
|
||
case BIT_XOR_EXPR:
|
||
opname = "^"; break;
|
||
case LROTATE_EXPR:
|
||
case RROTATE_EXPR:
|
||
opname = "rotate"; break;
|
||
}
|
||
error ("invalid operands to binary %s", opname);
|
||
}
|
||
|
||
/* Subroutine of build_binary_op, used for comparison operations.
|
||
See if the operands have both been converted from subword integer types
|
||
and, if so, perhaps change them both back to their original type.
|
||
|
||
The arguments of this function are all pointers to local variables
|
||
of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1,
|
||
RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE.
|
||
|
||
If this function returns nonzero, it means that the comparison has
|
||
a constant value. What this function returns is an expression for
|
||
that value. */
|
||
|
||
tree
|
||
shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
|
||
tree *op0_ptr, *op1_ptr;
|
||
tree *restype_ptr;
|
||
enum tree_code *rescode_ptr;
|
||
{
|
||
register tree type;
|
||
tree op0 = *op0_ptr;
|
||
tree op1 = *op1_ptr;
|
||
int unsignedp0, unsignedp1;
|
||
int real1, real2;
|
||
tree primop0, primop1;
|
||
enum tree_code code = *rescode_ptr;
|
||
|
||
/* Throw away any conversions to wider types
|
||
already present in the operands. */
|
||
|
||
primop0 = get_narrower (op0, &unsignedp0);
|
||
primop1 = get_narrower (op1, &unsignedp1);
|
||
|
||
/* Handle the case that OP0 does not *contain* a conversion
|
||
but it *requires* conversion to FINAL_TYPE. */
|
||
|
||
if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr)
|
||
unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0));
|
||
if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr)
|
||
unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1));
|
||
|
||
/* If one of the operands must be floated, we cannot optimize. */
|
||
real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE;
|
||
real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE;
|
||
|
||
/* If first arg is constant, swap the args (changing operation
|
||
so value is preserved), for canonicalization. */
|
||
|
||
if (TREE_CONSTANT (primop0))
|
||
{
|
||
register tree tem = primop0;
|
||
register int temi = unsignedp0;
|
||
primop0 = primop1;
|
||
primop1 = tem;
|
||
tem = op0;
|
||
op0 = op1;
|
||
op1 = tem;
|
||
*op0_ptr = op0;
|
||
*op1_ptr = op1;
|
||
unsignedp0 = unsignedp1;
|
||
unsignedp1 = temi;
|
||
temi = real1;
|
||
real1 = real2;
|
||
real2 = temi;
|
||
|
||
switch (code)
|
||
{
|
||
case LT_EXPR:
|
||
code = GT_EXPR;
|
||
break;
|
||
case GT_EXPR:
|
||
code = LT_EXPR;
|
||
break;
|
||
case LE_EXPR:
|
||
code = GE_EXPR;
|
||
break;
|
||
case GE_EXPR:
|
||
code = LE_EXPR;
|
||
break;
|
||
}
|
||
*rescode_ptr = code;
|
||
}
|
||
|
||
/* If comparing an integer against a constant more bits wide,
|
||
maybe we can deduce a value of 1 or 0 independent of the data.
|
||
Or else truncate the constant now
|
||
rather than extend the variable at run time.
|
||
|
||
This is only interesting if the constant is the wider arg.
|
||
Also, it is not safe if the constant is unsigned and the
|
||
variable arg is signed, since in this case the variable
|
||
would be sign-extended and then regarded as unsigned.
|
||
Our technique fails in this case because the lowest/highest
|
||
possible unsigned results don't follow naturally from the
|
||
lowest/highest possible values of the variable operand.
|
||
For just EQ_EXPR and NE_EXPR there is another technique that
|
||
could be used: see if the constant can be faithfully represented
|
||
in the other operand's type, by truncating it and reextending it
|
||
and see if that preserves the constant's value. */
|
||
|
||
if (!real1 && !real2
|
||
&& TREE_CODE (primop1) == INTEGER_CST
|
||
&& TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr))
|
||
{
|
||
int min_gt, max_gt, min_lt, max_lt;
|
||
tree maxval, minval;
|
||
/* 1 if comparison is nominally unsigned. */
|
||
int unsignedp = TREE_UNSIGNED (*restype_ptr);
|
||
tree val;
|
||
|
||
type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0));
|
||
|
||
maxval = TYPE_MAX_VALUE (type);
|
||
minval = TYPE_MIN_VALUE (type);
|
||
|
||
if (unsignedp && !unsignedp0)
|
||
*restype_ptr = signed_type (*restype_ptr);
|
||
|
||
if (TREE_TYPE (primop1) != *restype_ptr)
|
||
primop1 = convert (*restype_ptr, primop1);
|
||
if (type != *restype_ptr)
|
||
{
|
||
minval = convert (*restype_ptr, minval);
|
||
maxval = convert (*restype_ptr, maxval);
|
||
}
|
||
|
||
if (unsignedp && unsignedp0)
|
||
{
|
||
min_gt = INT_CST_LT_UNSIGNED (primop1, minval);
|
||
max_gt = INT_CST_LT_UNSIGNED (primop1, maxval);
|
||
min_lt = INT_CST_LT_UNSIGNED (minval, primop1);
|
||
max_lt = INT_CST_LT_UNSIGNED (maxval, primop1);
|
||
}
|
||
else
|
||
{
|
||
min_gt = INT_CST_LT (primop1, minval);
|
||
max_gt = INT_CST_LT (primop1, maxval);
|
||
min_lt = INT_CST_LT (minval, primop1);
|
||
max_lt = INT_CST_LT (maxval, primop1);
|
||
}
|
||
|
||
val = 0;
|
||
/* This used to be a switch, but Genix compiler can't handle that. */
|
||
if (code == NE_EXPR)
|
||
{
|
||
if (max_lt || min_gt)
|
||
val = integer_one_node;
|
||
}
|
||
else if (code == EQ_EXPR)
|
||
{
|
||
if (max_lt || min_gt)
|
||
val = integer_zero_node;
|
||
}
|
||
else if (code == LT_EXPR)
|
||
{
|
||
if (max_lt)
|
||
val = integer_one_node;
|
||
if (!min_lt)
|
||
val = integer_zero_node;
|
||
}
|
||
else if (code == GT_EXPR)
|
||
{
|
||
if (min_gt)
|
||
val = integer_one_node;
|
||
if (!max_gt)
|
||
val = integer_zero_node;
|
||
}
|
||
else if (code == LE_EXPR)
|
||
{
|
||
if (!max_gt)
|
||
val = integer_one_node;
|
||
if (min_gt)
|
||
val = integer_zero_node;
|
||
}
|
||
else if (code == GE_EXPR)
|
||
{
|
||
if (!min_lt)
|
||
val = integer_one_node;
|
||
if (max_lt)
|
||
val = integer_zero_node;
|
||
}
|
||
|
||
/* If primop0 was sign-extended and unsigned comparison specd,
|
||
we did a signed comparison above using the signed type bounds.
|
||
But the comparison we output must be unsigned.
|
||
|
||
Also, for inequalities, VAL is no good; but if the signed
|
||
comparison had *any* fixed result, it follows that the
|
||
unsigned comparison just tests the sign in reverse
|
||
(positive values are LE, negative ones GE).
|
||
So we can generate an unsigned comparison
|
||
against an extreme value of the signed type. */
|
||
|
||
if (unsignedp && !unsignedp0)
|
||
{
|
||
if (val != 0)
|
||
switch (code)
|
||
{
|
||
case LT_EXPR:
|
||
case GE_EXPR:
|
||
primop1 = TYPE_MIN_VALUE (type);
|
||
val = 0;
|
||
break;
|
||
|
||
case LE_EXPR:
|
||
case GT_EXPR:
|
||
primop1 = TYPE_MAX_VALUE (type);
|
||
val = 0;
|
||
break;
|
||
}
|
||
type = unsigned_type (type);
|
||
}
|
||
|
||
if (!max_gt && !unsignedp0)
|
||
{
|
||
/* This is the case of (char)x >?< 0x80, which people used to use
|
||
expecting old C compilers to change the 0x80 into -0x80. */
|
||
if (val == integer_zero_node)
|
||
warning ("comparison is always 0 due to limited range of data type");
|
||
if (val == integer_one_node)
|
||
warning ("comparison is always 1 due to limited range of data type");
|
||
}
|
||
|
||
if (!min_lt && unsignedp0)
|
||
{
|
||
/* This is the case of (unsigned char)x >?< -1 or < 0. */
|
||
if (val == integer_zero_node)
|
||
warning ("comparison is always 0 due to limited range of data type");
|
||
if (val == integer_one_node)
|
||
warning ("comparison is always 1 due to limited range of data type");
|
||
}
|
||
|
||
if (val != 0)
|
||
{
|
||
/* Don't forget to evaluate PRIMOP0 if it has side effects. */
|
||
if (TREE_SIDE_EFFECTS (primop0))
|
||
return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val);
|
||
return val;
|
||
}
|
||
|
||
/* Value is not predetermined, but do the comparison
|
||
in the type of the operand that is not constant.
|
||
TYPE is already properly set. */
|
||
}
|
||
else if (real1 && real2
|
||
&& (TYPE_PRECISION (TREE_TYPE (primop0))
|
||
== TYPE_PRECISION (TREE_TYPE (primop1))))
|
||
type = TREE_TYPE (primop0);
|
||
|
||
/* If args' natural types are both narrower than nominal type
|
||
and both extend in the same manner, compare them
|
||
in the type of the wider arg.
|
||
Otherwise must actually extend both to the nominal
|
||
common type lest different ways of extending
|
||
alter the result.
|
||
(eg, (short)-1 == (unsigned short)-1 should be 0.) */
|
||
|
||
else if (unsignedp0 == unsignedp1 && real1 == real2
|
||
&& TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)
|
||
&& TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr))
|
||
{
|
||
type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1));
|
||
type = signed_or_unsigned_type (unsignedp0
|
||
|| TREE_UNSIGNED (*restype_ptr),
|
||
type);
|
||
/* Make sure shorter operand is extended the right way
|
||
to match the longer operand. */
|
||
primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)),
|
||
primop0);
|
||
primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)),
|
||
primop1);
|
||
}
|
||
else
|
||
{
|
||
/* Here we must do the comparison on the nominal type
|
||
using the args exactly as we received them. */
|
||
type = *restype_ptr;
|
||
primop0 = op0;
|
||
primop1 = op1;
|
||
|
||
if (!real1 && !real2 && integer_zerop (primop1)
|
||
&& TREE_UNSIGNED (TREE_TYPE (primop0)))
|
||
{
|
||
tree value = 0;
|
||
switch (code)
|
||
{
|
||
case GE_EXPR:
|
||
if (extra_warnings)
|
||
warning ("unsigned value >= 0 is always 1");
|
||
value = integer_one_node;
|
||
break;
|
||
|
||
case LT_EXPR:
|
||
if (extra_warnings)
|
||
warning ("unsigned value < 0 is always 0");
|
||
value = integer_zero_node;
|
||
}
|
||
|
||
if (value != 0)
|
||
{
|
||
/* Don't forget to evaluate PRIMOP0 if it has side effects. */
|
||
if (TREE_SIDE_EFFECTS (primop0))
|
||
return build (COMPOUND_EXPR, TREE_TYPE (value),
|
||
primop0, value);
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
|
||
*op0_ptr = convert (type, primop0);
|
||
*op1_ptr = convert (type, primop1);
|
||
|
||
*restype_ptr = integer_type_node;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
|
||
or validate its data type for an `if' or `while' statement or ?..: exp.
|
||
|
||
This preparation consists of taking the ordinary
|
||
representation of an expression expr and producing a valid tree
|
||
boolean expression describing whether expr is nonzero. We could
|
||
simply always do build_binary_op (NE_EXPR, expr, integer_zero_node, 1),
|
||
but we optimize comparisons, &&, ||, and !.
|
||
|
||
The resulting type should always be `integer_type_node'. */
|
||
|
||
tree
|
||
truthvalue_conversion (expr)
|
||
tree expr;
|
||
{
|
||
register enum tree_code code;
|
||
|
||
if (TREE_CODE (expr) == ERROR_MARK)
|
||
return expr;
|
||
|
||
#if 0 /* This appears to be wrong for C++. */
|
||
/* These really should return error_mark_node after 2.4 is stable.
|
||
But not all callers handle ERROR_MARK properly. */
|
||
switch (TREE_CODE (TREE_TYPE (expr)))
|
||
{
|
||
case RECORD_TYPE:
|
||
error ("struct type value used where scalar is required");
|
||
return integer_zero_node;
|
||
|
||
case UNION_TYPE:
|
||
error ("union type value used where scalar is required");
|
||
return integer_zero_node;
|
||
|
||
case ARRAY_TYPE:
|
||
error ("array type value used where scalar is required");
|
||
return integer_zero_node;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
#endif /* 0 */
|
||
|
||
switch (TREE_CODE (expr))
|
||
{
|
||
/* It is simpler and generates better code to have only TRUTH_*_EXPR
|
||
or comparison expressions as truth values at this level. */
|
||
#if 0
|
||
case COMPONENT_REF:
|
||
/* A one-bit unsigned bit-field is already acceptable. */
|
||
if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1)))
|
||
&& TREE_UNSIGNED (TREE_OPERAND (expr, 1)))
|
||
return expr;
|
||
break;
|
||
#endif
|
||
|
||
case EQ_EXPR:
|
||
/* It is simpler and generates better code to have only TRUTH_*_EXPR
|
||
or comparison expressions as truth values at this level. */
|
||
#if 0
|
||
if (integer_zerop (TREE_OPERAND (expr, 1)))
|
||
return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0);
|
||
#endif
|
||
case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
|
||
case TRUTH_ANDIF_EXPR:
|
||
case TRUTH_ORIF_EXPR:
|
||
case TRUTH_AND_EXPR:
|
||
case TRUTH_OR_EXPR:
|
||
case TRUTH_XOR_EXPR:
|
||
case ERROR_MARK:
|
||
return expr;
|
||
|
||
case INTEGER_CST:
|
||
return integer_zerop (expr) ? integer_zero_node : integer_one_node;
|
||
|
||
case REAL_CST:
|
||
return real_zerop (expr) ? integer_zero_node : integer_one_node;
|
||
|
||
case ADDR_EXPR:
|
||
if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
|
||
return build (COMPOUND_EXPR, integer_type_node,
|
||
TREE_OPERAND (expr, 0), integer_one_node);
|
||
else
|
||
return integer_one_node;
|
||
|
||
case COMPLEX_EXPR:
|
||
return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))
|
||
? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
|
||
truthvalue_conversion (TREE_OPERAND (expr, 0)),
|
||
truthvalue_conversion (TREE_OPERAND (expr, 1)),
|
||
0);
|
||
|
||
case NEGATE_EXPR:
|
||
case ABS_EXPR:
|
||
case FLOAT_EXPR:
|
||
case FFS_EXPR:
|
||
/* These don't change whether an object is non-zero or zero. */
|
||
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
||
|
||
case LROTATE_EXPR:
|
||
case RROTATE_EXPR:
|
||
/* These don't change whether an object is zero or non-zero, but
|
||
we can't ignore them if their second arg has side-effects. */
|
||
if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
|
||
return build (COMPOUND_EXPR, integer_type_node, TREE_OPERAND (expr, 1),
|
||
truthvalue_conversion (TREE_OPERAND (expr, 0)));
|
||
else
|
||
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
||
|
||
case COND_EXPR:
|
||
/* Distribute the conversion into the arms of a COND_EXPR. */
|
||
return fold (build (COND_EXPR, integer_type_node, TREE_OPERAND (expr, 0),
|
||
truthvalue_conversion (TREE_OPERAND (expr, 1)),
|
||
truthvalue_conversion (TREE_OPERAND (expr, 2))));
|
||
|
||
case CONVERT_EXPR:
|
||
/* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE,
|
||
since that affects how `default_conversion' will behave. */
|
||
if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
|
||
|| TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
|
||
break;
|
||
/* fall through... */
|
||
case NOP_EXPR:
|
||
/* If this is widening the argument, we can ignore it. */
|
||
if (TYPE_PRECISION (TREE_TYPE (expr))
|
||
>= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))
|
||
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
||
break;
|
||
|
||
case MINUS_EXPR:
|
||
/* With IEEE arithmetic, x - x may not equal 0, so we can't optimize
|
||
this case. */
|
||
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
|
||
&& TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
|
||
break;
|
||
/* fall through... */
|
||
case BIT_XOR_EXPR:
|
||
/* This and MINUS_EXPR can be changed into a comparison of the
|
||
two objects. */
|
||
if (TREE_TYPE (TREE_OPERAND (expr, 0))
|
||
== TREE_TYPE (TREE_OPERAND (expr, 1)))
|
||
return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
|
||
TREE_OPERAND (expr, 1), 1);
|
||
return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
|
||
fold (build1 (NOP_EXPR,
|
||
TREE_TYPE (TREE_OPERAND (expr, 0)),
|
||
TREE_OPERAND (expr, 1))), 1);
|
||
|
||
case MODIFY_EXPR:
|
||
if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR)
|
||
warning ("suggest parentheses around assignment used as truth value");
|
||
break;
|
||
}
|
||
|
||
if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
|
||
return (build_binary_op
|
||
((TREE_SIDE_EFFECTS (expr)
|
||
? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
|
||
truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)),
|
||
truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)),
|
||
0));
|
||
|
||
return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
|
||
}
|
||
|
||
/* Read the rest of a #-directive from input stream FINPUT.
|
||
In normal use, the directive name and the white space after it
|
||
have already been read, so they won't be included in the result.
|
||
We allow for the fact that the directive line may contain
|
||
a newline embedded within a character or string literal which forms
|
||
a part of the directive.
|
||
|
||
The value is a string in a reusable buffer. It remains valid
|
||
only until the next time this function is called. */
|
||
|
||
char *
|
||
get_directive_line (finput)
|
||
register FILE *finput;
|
||
{
|
||
static char *directive_buffer = NULL;
|
||
static unsigned buffer_length = 0;
|
||
register char *p;
|
||
register char *buffer_limit;
|
||
register int looking_for = 0;
|
||
register int char_escaped = 0;
|
||
|
||
if (buffer_length == 0)
|
||
{
|
||
directive_buffer = (char *)xmalloc (128);
|
||
buffer_length = 128;
|
||
}
|
||
|
||
buffer_limit = &directive_buffer[buffer_length];
|
||
|
||
for (p = directive_buffer; ; )
|
||
{
|
||
int c;
|
||
|
||
/* Make buffer bigger if it is full. */
|
||
if (p >= buffer_limit)
|
||
{
|
||
register unsigned bytes_used = (p - directive_buffer);
|
||
|
||
buffer_length *= 2;
|
||
directive_buffer
|
||
= (char *)xrealloc (directive_buffer, buffer_length);
|
||
p = &directive_buffer[bytes_used];
|
||
buffer_limit = &directive_buffer[buffer_length];
|
||
}
|
||
|
||
c = getc (finput);
|
||
|
||
/* Discard initial whitespace. */
|
||
if ((c == ' ' || c == '\t') && p == directive_buffer)
|
||
continue;
|
||
|
||
/* Detect the end of the directive. */
|
||
if (c == '\n' && looking_for == 0)
|
||
{
|
||
ungetc (c, finput);
|
||
c = '\0';
|
||
}
|
||
|
||
*p++ = c;
|
||
|
||
if (c == 0)
|
||
return directive_buffer;
|
||
|
||
/* Handle string and character constant syntax. */
|
||
if (looking_for)
|
||
{
|
||
if (looking_for == c && !char_escaped)
|
||
looking_for = 0; /* Found terminator... stop looking. */
|
||
}
|
||
else
|
||
if (c == '\'' || c == '"')
|
||
looking_for = c; /* Don't stop buffering until we see another
|
||
another one of these (or an EOF). */
|
||
|
||
/* Handle backslash. */
|
||
char_escaped = (c == '\\' && ! char_escaped);
|
||
}
|
||
}
|
||
|
||
/* Make a variant type in the proper way for C/C++, propagating qualifiers
|
||
down to the element type of an array. */
|
||
|
||
tree
|
||
c_build_type_variant (type, constp, volatilep)
|
||
tree type;
|
||
int constp, volatilep;
|
||
{
|
||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||
{
|
||
tree real_main_variant = TYPE_MAIN_VARIANT (type);
|
||
int permanent = TREE_PERMANENT (type);
|
||
|
||
if (permanent)
|
||
push_obstacks (&permanent_obstack, &permanent_obstack);
|
||
type = build_array_type (c_build_type_variant (TREE_TYPE (type),
|
||
constp, volatilep),
|
||
TYPE_DOMAIN (type));
|
||
TYPE_MAIN_VARIANT (type) = real_main_variant;
|
||
if (permanent)
|
||
pop_obstacks ();
|
||
}
|
||
return build_type_variant (type, constp, volatilep);
|
||
}
|