2007-04-21
2007-04-21 20:08:26 by Henrik Grubbström (Grubba) <grubba@grubba.org>
-
91291224b9be8889fca5411e9752d15b8e6d1f3c
(312 lines)
(+277/-35)
[
Show
| Annotate
]
Branch: 7.9
Implemented PIKE_T_ATTRIBUTE type nodes.
Added push_type_attribute().
Added use and documentation of CompilationHandler()->handle_attribute_constant() when attributed parameters are encountered with constant values.
low_new_check_call() now takes an svalue as the fourth argument.
Fixed bug in get_first_argument_type() for anded function types.
Added register_attribute_handler().
Added efuns __register_attribute_handler() and __handle_attribute_constant().
The efun __low_check_call() now takes a fourth argument.
Rev: src/builtin_functions.c:1.637
Rev: src/pike_types.c:1.293
Rev: src/pike_types.h:1.105
2:
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
- || $Id: pike_types.c,v 1.292 2007/04/21 12:42:20 grubba Exp $
+ || $Id: pike_types.c,v 1.293 2007/04/21 20:08:26 grubba Exp $
*/
#include "global.h"
211:
* SCOPE num vars (int) type
* ASSIGN variable (int) type
* NAME name (string) type
+ * ATTRIBUTE name (string) type
* FUNCTION type FUNCTION|MANY
* MANY many type return type
* RING type type
310:
t = (struct pike_type *)debug_malloc_pass(cdr);
goto loop;
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_NAME:
free_string((struct pike_string *)car);
t = (struct pike_type *)debug_malloc_pass(cdr);
469:
free_type((struct pike_type *)debug_malloc_pass(cdr));
break;
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_NAME:
free_string((struct pike_string *)debug_malloc_pass(car));
free_type((struct pike_type *)debug_malloc_pass(cdr));
567: Inside #if defined(DEBUG_MALLOC)
debug_malloc_pass(cdr);
break;
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_NAME:
debug_malloc_pass(car);
debug_malloc_pass(cdr);
737:
TYPE_STACK_DEBUG("push_assign_type");
}
+ void debug_push_type_attribute(struct pike_string *attr)
+ {
+ /* fprintf(stderr, "push_type_attribute(\"%s\")\n", attr->str); */
+ add_ref(attr);
+ *Pike_compiler->type_stackp = mk_type(PIKE_T_ATTRIBUTE,
+ (void *)attr,
+ *Pike_compiler->type_stackp,
+ PT_COPY_CDR);
+ TYPE_STACK_DEBUG("push_type_name");
+ }
+
void debug_push_type_name(struct pike_string *name)
{
/* fprintf(stderr, "push_type_name(\"%s\")\n", name->str); */
879:
case T_ASSIGN:
case T_INT:
case T_OBJECT:
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_NAME:
default:
/* Should not occur. */
929:
Pike_compiler->type_stackp--;
#ifdef PIKE_DEBUG
- if ((top->type != expected) && (top->type != PIKE_T_NAME)) {
+ if ((top->type != expected) &&
+ (top->type != PIKE_T_NAME) &&
+ (top->type != PIKE_T_ATTRIBUTE)) {
Pike_fatal("Unexpected type on stack: %d (expected %d)\n", top->type, expected);
}
#endif /* PIKE_DEBUG */
981:
case '9':
/* Leaf */
break;
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_NAME:
/* Pop the name and recurse. */
push_finished_type(top->cdr);
1135:
push_type(T_AND);
}
}
+ } else if (type->type == PIKE_T_ATTRIBUTE) {
+ /* Keep the attribute. */
+ push_finished_type_with_markers(type->cdr, markers, marker_set);
+ push_type_attribute((struct pike_string *)type->car);
} else {
if (type->cdr) {
/* We want to keep markers that have assigns. */
1757:
fprintf(stderr, " }");
break;
+ case PIKE_T_ATTRIBUTE:
+ fprintf(stderr, "attribute(%s, ",
+ ((struct pike_string *)s->car)->str);
+ simple_describe_type(s->cdr);
+ fprintf(stderr, ")");
+ break;
+
case T_SCOPE:
fprintf(stderr, "scope(%"PRINTPTRDIFFT"d, ", CAR_TO_INT(s));
simple_describe_type(s->cdr);
2030:
}
break;
+ case PIKE_T_ATTRIBUTE:
+ if (!((struct pike_string *)t->car)->size_shift) {
+ my_strcat("attribute(");
+ my_binary_strcat(((struct pike_string *)t->car)->str,
+ ((struct pike_string *)t->car)->len);
+ my_strcat(", ");
+ my_describe_type(t->cdr);
+ my_strcat(")");
+ } else {
+ my_describe_type(t->cdr);
+ }
+ break;
+
case T_FUNCTION:
case T_MANY:
{
2215:
case T_SCOPE:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return compile_type_to_runtime_type(t->cdr);
case T_MANY:
2955:
return low_match_types(a->car, b, flags);
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_match_types(a->cdr, b, flags);
case T_NOT:
3062:
return low_match_types(a, b->car, flags);
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_match_types(a, b->cdr, flags);
case T_NOT:
3487:
goto recurse;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
a = a->cdr;
goto recurse;
3607:
goto recurse;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
b = b->cdr;
goto recurse;
4086:
case PIKE_T_NAME:
return low_get_return_type(a->cdr, b);
+ case PIKE_T_ATTRIBUTE:
+ if (low_get_return_type(a->cdr, b)) {
+ push_type_attribute((struct pike_string *)a->car);
+ return 1;
+ }
+ return 0;
+
case T_ARRAY:
tmp = low_get_return_type(a->car, b);
if(!tmp) return 0;
4167:
return zero_type_string;
}
- while(t->type == PIKE_T_NAME) {
+ while((t->type == PIKE_T_NAME) ||
+ (t->type == PIKE_T_ATTRIBUTE)) {
t = t->cdr;
}
- while(index_type->type == PIKE_T_NAME) {
+ while((index_type->type == PIKE_T_NAME) ||
+ (index_type->type == PIKE_T_ATTRIBUTE)) {
index_type = index_type->cdr;
}
4353:
struct pike_type *tmp;
struct program *p;
- while(t->type == PIKE_T_NAME) {
+ while((t->type == PIKE_T_NAME) ||
+ (t->type == PIKE_T_ATTRIBUTE)) {
t = t->cdr;
}
if (index1_type)
- while(index1_type->type == PIKE_T_NAME) {
+ while((index1_type->type == PIKE_T_NAME) ||
+ (index1_type->type == PIKE_T_ATTRIBUTE)) {
index1_type = index1_type->cdr;
}
if (index2_type)
- while(index2_type->type == PIKE_T_NAME) {
+ while((index2_type->type == PIKE_T_NAME) ||
+ (index2_type->type == PIKE_T_ATTRIBUTE)) {
index2_type = index2_type->cdr;
}
4650:
return low_key_type(t->cdr, n);
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_key_type(t->cdr, n);
case T_ARRAY:
4695:
return low_check_indexing(type->car, index_type, n) != 1;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_check_indexing(type->cdr, index_type, n);
case T_ARRAY:
4776:
return num<num2?num:num2;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_count_arguments(q->cdr);
default: return 0x7fffffff;
4820:
default: return 0;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return low_minimum_arguments(q->cdr);
case T_FUNCTION:
4979:
soft_type = soft_type->cdr;
goto loop;
/* FIXME: TUPLE, RING */
+ case PIKE_T_ATTRIBUTE:
+ if ((res = soft_cast(soft_type->cdr, orig_type, flags))) {
+ type_stack_mark();
+ push_finished_type(res);
+ push_type_attribute((struct pike_string *)soft_type->car);
+ free_type(res);
+ res = pop_unfinished_type();
+ }
+ return res;
case T_MIXED:
if (flags & SOFT_WEAKER) {
copy_pike_type(res, soft_type);
5013:
case PIKE_T_NAME:
orig_type = orig_type->cdr;
goto loop2;
+ case PIKE_T_ATTRIBUTE:
+ if ((res = soft_cast(soft_type, orig_type->cdr, flags))) {
+ type_stack_mark();
+ push_finished_type(res);
+ push_type_attribute((struct pike_string *)orig_type->car);
+ free_type(res);
+ res = pop_unfinished_type();
+ }
+ return res;
case T_MIXED:
if (flags & SOFT_WEAKER) {
copy_pike_type(res, orig_type);
5271:
return res;
}
+ /*! @class CompilationHandler
+ */
+
+ /*! @decl type handle_attribute_constant(string attr, mixed value, @
+ *! type arg_type, type cont_type)
+ *!
+ *! Handle constant arguments to attributed parameter types.
+ *!
+ *! This function is called when a function parameter with the
+ *! attribute @[attr] is called with the constant value @[value].
+ *!
+ *! This function is typically used to perform specialized
+ *! argument checking.
+ *!
+ *! @param arg_type
+ *! The declared type of the parameter.
+ *!
+ *! @param cont_type
+ *! The type for the continued function.
+ *!
+ *! @returns
+ *! Returns a new continuation type if it succeeded in strengthening
+ *! the type.
+ *!
+ *! Returns @expr{UNDEFINED@} otherwise (this is not an error indication).
+ */
+
+ /*! @endclass
+ */
+
/* Check whether arg_type may be used as the type of the first argument
* in a call to fun_type.
*
5282:
*/
static struct pike_type *lower_new_check_call(struct pike_type *fun_type,
struct pike_type *arg_type,
- INT32 flags
+ INT32 flags,
+ struct svalue *sval
#ifdef PIKE_TYPE_DEBUG
, INT32 indent
#define CHECK_CALL_ARGS , indent+1
5302: Inside #if defined(PIKE_DEBUG)
simple_describe_type(fun_type);
fprintf(stderr, ", ");
simple_describe_type(arg_type);
- fprintf(stderr, ", 0x%04x)...\n", flags);
+ fprintf(stderr, ", 0x%04x, %p)...\n", flags, sval);
}
#endif /* PIKE_DEBUG */
5317:
case T_SCOPE:
case T_ASSIGN:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
fun_type = fun_type->cdr;
goto loop;
-
+
case T_OR:
- res = lower_new_check_call(fun_type->car, arg_type, flags CHECK_CALL_ARGS);
+ res = lower_new_check_call(fun_type->car, arg_type, flags, sval CHECK_CALL_ARGS);
if (!res) {
- res = lower_new_check_call(fun_type->cdr, arg_type, flags CHECK_CALL_ARGS);
+ res = lower_new_check_call(fun_type->cdr, arg_type, flags, sval CHECK_CALL_ARGS);
break;
}
- tmp = lower_new_check_call(fun_type->cdr, arg_type, flags CHECK_CALL_ARGS);
+ tmp = lower_new_check_call(fun_type->cdr, arg_type, flags, sval CHECK_CALL_ARGS);
if (!tmp) break;
res = or_pike_types(tmp2 = res, tmp, 1);
free_type(tmp);
5334:
break;
case T_AND:
- res = lower_new_check_call(fun_type->car, arg_type, flags CHECK_CALL_ARGS);
+ res = lower_new_check_call(fun_type->car, arg_type, flags, sval CHECK_CALL_ARGS);
if (!res) break;
- tmp = lower_new_check_call(fun_type->cdr, arg_type, flags CHECK_CALL_ARGS);
+ tmp = lower_new_check_call(fun_type->cdr, arg_type, flags, sval CHECK_CALL_ARGS);
if (!tmp) {
free_type(res);
res = NULL;
5380:
push_finished_type(arg_type);
push_type(T_NOT);
arg_type = pop_unfinished_type();
- res = lower_new_check_call(fun_type->car, arg_type, flags CHECK_CALL_ARGS);
+ res = lower_new_check_call(fun_type->car, arg_type, flags, sval CHECK_CALL_ARGS);
free_type(arg_type);
if (res) {
/* Move the inversion back to the function type. */
5435:
} else {
fun_type = zzap_function_return(tmp, CDR_TO_INT(fun_type->car));
}
- res = lower_new_check_call(fun_type, arg_type, flags CHECK_CALL_ARGS);
+ res = lower_new_check_call(fun_type, arg_type, flags, sval CHECK_CALL_ARGS);
free_type(fun_type);
break;
5489: Inside #if defined(PIKE_DEBUG)
fprintf(stderr, ".\n");
}
#endif /* PIKE_DEBUG */
- if (!low_pike_types_le(arg_type, fun_type->car, 0, 0) &&
+ if (!low_pike_types_le(arg_type, tmp2 = fun_type->car, 0, 0) &&
((flags & CALL_STRICT) ||
- !low_match_types(arg_type, fun_type->car, NO_SHORTCUTS))) {
+ !low_match_types(arg_type, tmp2, NO_SHORTCUTS))) {
/* No match. */
#ifdef PIKE_DEBUG
if (l_flag>2) {
5519:
break;
}
}
+ type_stack_mark();
+ push_finished_type_with_markers(fun_type, b_markers, PT_FLAG_MARKER);
+ res = pop_unfinished_type();
+ if (tmp) free_type(tmp);
+
+ if ((tmp2->type == PIKE_T_ATTRIBUTE) &&
+ (Pike_compiler->compiler_pass == 2) &&
+ sval) {
+ /* Perform extra argument checking based on the attribute. */
+ /* FIXME: Support multiple attributes. */
+ ref_push_string((struct pike_string *)tmp2->car);
+ push_svalue(sval);
+ ref_push_type_value(tmp2->cdr);
+ ref_push_type_value(res);
+ if (safe_apply_handler("handle_attribute_constant", error_handler,
+ compat_handler, 4, 0)) {
+ if ((Pike_sp[-1].type == PIKE_T_TYPE)) {
+ type_stack_mark();
+ push_finished_type(Pike_sp[-1].u.type);
+ push_finished_type(res);
+ push_type(T_AND);
+ free_type(res);
+ res = pop_unfinished_type();
+ }
+ pop_stack();
+ }
+ }
#ifdef PIKE_DEBUG
if (l_flag>2) {
fprintf(stderr, "%*sSuccess.\n", indent*2+2, "");
}
#endif /* PIKE_DEBUG */
- type_stack_mark();
- push_finished_type_with_markers(fun_type, b_markers, PT_FLAG_MARKER);
- res = pop_unfinished_type();
- if (tmp) free_type(tmp);
+
break;
default:
/* Not a callable. */
5570:
/* Check whether arg_type may be used as the type of the first argument
* in a call to fun_type.
*
+ * If the argument is a constant, sval will contain a pointer to it.
+ *
* Returns NULL on failure.
*
* Returns continuation function type on success.
*/
struct pike_type *low_new_check_call(struct pike_type *fun_type,
struct pike_type *arg_type,
- INT32 flags)
+ INT32 flags,
+ struct svalue *sval)
{
struct pike_type *tmp;
struct pike_type *tmp2;
5612:
goto loop;
case T_OR:
- if (!(tmp = low_new_check_call(fun_type, arg_type->car, flags))) {
+ if (!(tmp = low_new_check_call(fun_type, arg_type->car, flags, sval))) {
if (flags & CALL_STRICT) {
return NULL;
}
arg_type = arg_type->cdr;
goto loop;
}
- if (!(tmp2 = low_new_check_call(fun_type, arg_type->cdr, flags))) {
+ if (!(tmp2 = low_new_check_call(fun_type, arg_type->cdr, flags, sval))) {
if (flags & CALL_STRICT) {
return NULL;
}
5631:
return res;
case T_AND:
- if (!(tmp = low_new_check_call(fun_type, arg_type->car, flags))) {
+ if (!(tmp = low_new_check_call(fun_type, arg_type->car, flags, sval))) {
return NULL;
}
- if (!(tmp2 = low_new_check_call(fun_type, arg_type->cdr, flags))) {
+ if (!(tmp2 = low_new_check_call(fun_type, arg_type->cdr, flags, sval))) {
free_type(tmp);
return NULL;
}
5651:
break;
}
- if (!(tmp = lower_new_check_call(fun_type, arg_type, flags
+ if (!(tmp = lower_new_check_call(fun_type, arg_type, flags, sval
#ifdef PIKE_TYPE_DEBUG
, 0
#endif
5695:
case PIKE_T_SCOPE:
case T_ASSIGN:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
fun_type = fun_type->cdr;
goto loop;
5946:
case PIKE_T_SCOPE:
case T_ASSIGN:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
case PIKE_T_RING:
fun_type = fun_type->cdr;
goto loop;
5971:
res = NULL;
break;
}
- /* NOTE: OR and not AND!
+ /* NOTE: OR and not AND in some cases!
*
* !function(!string:mixed)&function(string|int:string)
* ==>
* string | string|int
-
+ *
+ * This is however not true in the case where neither is inverted:
+ *
+ * function(attribute(sprintf_args, mixed)...:string) &
+ * function(object|string:string)
+ * ==>
+ * attribute(sprintf_args, mixed) & object|string
*/
-
+ if ((fun_type->car->type == T_NOT) == (fun_type->cdr->type == T_NOT)) {
+ res = and_pike_types(tmp2 = res, tmp);
+ } else {
res = or_pike_types(tmp2 = res, tmp, 1);
-
+ }
free_type(tmp);
free_type(tmp2);
break;
6052:
{
struct pike_type *tmp = NULL;
struct pike_type *res = NULL;
+ struct svalue *sval = NULL;
int flags = 0;
debug_malloc_touch(fun_type);
6069:
(*argno)++;
+ if (args->token == F_CONSTANT) {
+ sval = &args->u.sval;
+ }
+
#ifdef PIKE_DEBUG
if (l_flag>2) {
fprintf(stderr, " Checking argument #%d... ", *argno);
simple_describe_type(args->type);
-
+ if (sval) {
+ fprintf(stderr, "\n Constant of type %s", get_name_of_type(sval->type));
+ }
fprintf(stderr, "\n fun_type: ");
simple_describe_type(fun_type);
}
6099:
/* Loop until we get a stable fun_type, or it's an invalid argument. */
while ((fun_type = low_new_check_call(debug_malloc_pass(prev),
debug_malloc_pass(args->type),
- flags)) &&
+ flags, sval)) &&
(fun_type != prev) && --cnt) {
#ifdef PIKE_DEBUG
6152:
#endif /* PIKE_DEBUG */
return res;
- } else if ((res = low_new_check_call(fun_type, args->type, flags))) {
+ } else if ((res = low_new_check_call(fun_type, args->type, flags, sval))) {
/* OK. */
#ifdef PIKE_DEBUG
if (l_flag>2) {
6161:
#endif /* PIKE_DEBUG */
if (lex.pragmas & ID_STRICT_TYPES) {
if (!(tmp = low_new_check_call(fun_type, args->type,
- flags|CALL_STRICT))) {
+ flags|CALL_STRICT, sval))) {
yywarning("Type mismatch in argument %d to %S.",
*argno, fun_name);
if ((tmp = get_first_arg_type(fun_type, 0))) {
6193:
/* Try advancing with the suggested type, so that we can check
* the rest of the arguments.
*/
- if ((tmp2 = low_new_check_call(fun_type, tmp, flags))) {
+ if ((tmp2 = low_new_check_call(fun_type, tmp, flags, NULL))) {
/* Succeeded. */
free_type(fun_type);
free_type(tmp);
6268:
case PIKE_T_NAME:
return zzap_function_return(a->cdr, id);
+ case PIKE_T_ATTRIBUTE:
+ {
+ struct pike_type *res;
+ if ((res = zzap_function_return(a->cdr, id))) {
+ type_stack_mark();
+ push_finished_type(res);
+ push_type_attribute((struct pike_string *)a->car);
+ free_type(res);
+ res = pop_unfinished_type();
+ }
+ return res;
+ }
+
case T_MIXED:
/* I wonder when this occurrs, but apparently it does... */
/* FIXME: */
6528:
return 0;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
return type_may_overload(type->cdr, lfun);
case PIKE_T_RING:
6722:
break;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
{
int size_shift = type_string[1] & 0x3;
struct pike_string *str;
6794:
}
}
low_make_pike_type(type_string + 2 + bytes + (1<<size_shift), cont);
+ if (type_string[0] == PIKE_T_NAME) {
push_type_name(str = end_shared_string(str));
-
+ } else {
+ push_type_attribute(str = end_shared_string(str));
+ }
free_string(str);
break;
}
6833:
return 0;
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
case T_SCOPE:
case T_ASSIGN:
type = type->cdr;
6886:
my_putchar(t->type);
/* FALL_THROUGH */
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
t = t->car;
goto recurse;
7039:
#endif /* DEBUG_MALLOC */
#endif /* 0 */
+ static struct mapping *builtin_attributes = NULL;
+
+ void register_attribute_handler(struct pike_string *attr,
+ struct svalue *handler)
+ {
+ mapping_string_insert(builtin_attributes, attr, handler);
+ }
+
+ static void f___register_attribute_handler(INT32 args)
+ {
+ if (args < 2) SIMPLE_TOO_FEW_ARGS_ERROR("__register_attribute_handler", 2);
+ if (args > 2) {
+ pop_n_elems(args-2);
+ args = 2;
+ }
+ if (Pike_sp[-2].type != PIKE_T_STRING) {
+ SIMPLE_BAD_ARG_ERROR("__register_attribute_handler", 1, "string");
+ }
+ mapping_insert(builtin_attributes, Pike_sp-2, Pike_sp-1);
+ pop_n_elems(args);
+ }
+
+ static void f___handle_attribute_constant(INT32 args)
+ {
+ struct svalue *sval;
+ if (args < 4) SIMPLE_TOO_FEW_ARGS_ERROR("__handle_attribute_constant", 4);
+ if (args > 4) {
+ pop_n_elems(args-4);
+ args = 4;
+ }
+ if (Pike_sp[-4].type != PIKE_T_STRING) {
+ SIMPLE_BAD_ARG_ERROR("__handle_attribute_constant", 1, "string");
+ }
+ if ((sval = low_mapping_lookup(builtin_attributes, Pike_sp-4))) {
+ apply_svalue(sval, 4);
+ } else {
+ pop_n_elems(args);
+ push_undefined();
+ }
+ }
+
void init_types(void)
{
/* Initialize hashtable here. */
7067:
tFuncV(tNone,tZero,tOr(tMix,tVoid))));
//add_ref(weak_type_string); /* LEAK */
+ builtin_attributes = allocate_mapping(20);
+
+ ADD_EFUN("__register_attribute_handler", f___register_attribute_handler,
+ tFunc(tSetvar(0, tStr)
+ tFunc(tVar(0) tMix tType(tMix) tType(tMix), tType(tMix)),
+ tVoid), 0);
+
+ ADD_EFUN("__handle_attribute_constant", f___handle_attribute_constant,
+ tFunc(tStr tMix tType(tMix) tType(tMix), tType(tMix)), 0);
+
#if 0
#ifdef DEBUG_MALLOC
dmalloc_gc_callback = add_gc_callback(gc_mark_external_types, NULL, NULL);
7085:
}
#endif /* DEBUG_MALLOC */
+ if (builtin_attributes) {
+ free_mapping(builtin_attributes);
+ builtin_attributes = NULL;
+ }
+
clear_markers();
free_type(string0_type_string);
7160: Inside #if 0 and #if defined(PIKE_DEBUG)
case PIKE_T_SCOPE:
case T_ASSIGN:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
if (t->cdr) gc_mark_type_as_referenced(t->cdr);
break;
case PIKE_T_FUNCTION:
7253: Inside #if 0 and #if defined(PIKE_DEBUG)
case PIKE_T_SCOPE:
case T_ASSIGN:
case PIKE_T_NAME:
+ case PIKE_T_ATTRIBUTE:
if (t->cdr) gc_cycle_check_type(t->cdr, 0);
break;
case PIKE_T_FUNCTION: