pike.git / src / pike_types.c

version» Context lines:

pike.git/src/pike_types.c:1:   /*   || 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"   #include <ctype.h>   #include "svalue.h"   #include "pike_types.h"   #include "stralloc.h"   #include "stuff.h"   #include "array.h"   #include "program.h"
pike.git/src/pike_types.c:204:   /*    * New and improved type representation system.    *    * This representation is new in Pike 7.3.    *    * Node: Car: Cdr:    * ---------------------------------------------    * 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    * TUPLE type type    * MAPPING index type value type    * OR type (not OR) type    * AND type type    * ARRAY type -    * MULTISET type -    * NOT type -
pike.git/src/pike_types.c:303:    /* Free car */    t = (struct pike_type *)debug_malloc_pass(car);    goto loop;       case T_SCOPE:    case T_ASSIGN:    /* Free cdr */    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);    goto loop;      #ifdef PIKE_DEBUG    case '0':    case '1':    case '2':    case '3':
pike.git/src/pike_types.c:462:    /* Free car */    free_type((struct pike_type *)debug_malloc_pass(car));    break;       case T_SCOPE:    case T_ASSIGN:    /* Free cdr */    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));    break;      #ifdef PIKE_DEBUG    case '0':    case '1':    case '2':    case '3':
pike.git/src/pike_types.c:560: Inside #if defined(DEBUG_MALLOC)
   debug_malloc_pass(car);    break;       case T_ASSIGN:    t->flags |= PT_FLAG_ASSIGN_0 << PTR_TO_INT(car);    /* FALL_THROUGH */    case T_SCOPE:    debug_malloc_pass(cdr);    break;    +  case PIKE_T_ATTRIBUTE:    case PIKE_T_NAME:    debug_malloc_pass(car);    debug_malloc_pass(cdr);    break;       case '0':    case '1':    case '2':    case '3':    case '4':
pike.git/src/pike_types.c:730:    }   #endif /* PIKE_DEBUG */       *Pike_compiler->type_stackp = mk_type(T_ASSIGN,    (void *)(ptrdiff_t)marker,    *Pike_compiler->type_stackp,    PT_COPY_CDR);    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); */    add_ref(name);    *Pike_compiler->type_stackp = mk_type(PIKE_T_NAME,    (void *)name,    *Pike_compiler->type_stackp,    PT_COPY_CDR);    TYPE_STACK_DEBUG("push_type_name");   }
pike.git/src/pike_types.c:872:    /* Make a new type of the top type, and put it in car. */    *Pike_compiler->type_stackp = mk_type(type,    *Pike_compiler->type_stackp, NULL,    PT_COPY_CAR);    break;       case T_SCOPE:    case T_ASSIGN:    case T_INT:    case T_OBJECT: +  case PIKE_T_ATTRIBUTE:    case PIKE_T_NAME:    default:    /* Should not occur. */    Pike_fatal("Unsupported argument to push_type(): %d\n", type);    break;       case T_STRING:    Pike_fatal("String types should not be created with push_type().\n");    break;   
pike.git/src/pike_types.c:922:    struct pike_type *top;    if(Pike_compiler->type_stackp<type_stack)    Pike_fatal("Type stack underflow\n");       top = *(Pike_compiler->type_stackp);    /* Special case... */    if (top->type == T_MIXED) return; /* Probably due to an earlier error */       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 */    /* OPTIMIZE: It looks like this function is always called with    * expected == T_ARRAY.    */    switch(top->type) {    case T_FUNCTION:    case T_MANY:    case T_TUPLE:
pike.git/src/pike_types.c:974:    case '2':    case '3':    case '4':    case '5':    case '6':    case '7':    case '8':    case '9':    /* Leaf */    break; +  case PIKE_T_ATTRIBUTE:    case PIKE_T_NAME:    /* Pop the name and recurse. */    push_finished_type(top->cdr);    pop_type_stack(expected);    break;    default:    Pike_error("pop_type_stack(): Unhandled node type: %d\n", top->type);    }    free_type(top);   
pike.git/src/pike_types.c:1128:    if (peek_type_stack() == zero_type_string) {    free_type(pop_unfinished_type());    free_type(pop_unfinished_type());    push_finished_type(zero_type_string);    } else {    pop_stack_mark();    pop_stack_mark();    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. */    push_finished_type_with_markers(type->cdr, markers,    marker_set |    (type->car->flags & PT_FLAG_ASSIGN));    }    /* In all other cases type->car will be a valid node. */    push_finished_type_with_markers(type->car, markers, marker_set);    /* push_type has sufficient magic to recreate the type. */
pike.git/src/pike_types.c:1750:    case '5': case '6': case '7': case '8': case '9':    fprintf(stderr, "%d", s->type-'0');    break;       case PIKE_T_NAME:    fprintf(stderr, "{ %s = ", ((struct pike_string *)s->car)->str);    simple_describe_type(s->cdr);    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);    fprintf(stderr, ")");    break;    case T_TUPLE:    fprintf(stderr, "tuple(");    simple_describe_type(s->car);    fprintf(stderr, ", ");    simple_describe_type(s->cdr);
pike.git/src/pike_types.c:2023:    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 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:    {    int s;    my_strcat("function");    if(t->type == T_MANY &&    t->cdr->type == T_OR &&    ((t->cdr->car->type == T_MIXED && t->cdr->cdr->type == T_VOID) ||    (t->cdr->cdr->type == T_MIXED && t->cdr->car->type == T_VOID)) &&    (t->car->type == T_ZERO ||
pike.git/src/pike_types.c:2208:    /* FIXME: Shouldn't occur/should be converted to array? */    /* FALL_THROUGH */    default:    return T_MIXED;       case T_ZERO:    return T_INT;       case T_SCOPE:    case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    return compile_type_to_runtime_type(t->cdr);       case T_MANY:    return T_FUNCTION;       case T_ARRAY:    case T_MAPPING:    case T_MULTISET:       case T_OBJECT:
pike.git/src/pike_types.c:2948:    low_match_types(a->cdr, b, flags);    return ret;    }else{    return low_match_types(a->cdr, b, flags);    }       case PIKE_T_RING:    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:    if(low_match_types(a->car, b, (flags ^ B_EXACT ) | NO_MAX_ARGS))    return 0;    return a;       case T_ASSIGN:    ret = low_match_types(a->cdr, b, flags);    if(ret && (b->type != T_VOID))
pike.git/src/pike_types.c:3055:    low_match_types(a, b->cdr, flags);    return ret;    }else{    return low_match_types(a, b->cdr, flags);    }       case PIKE_T_RING:    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:    if(low_match_types(a, b->car, (flags ^ A_EXACT ) | NO_MAX_ARGS))    return 0;    return a;       case T_ASSIGN:    ret = low_match_types(a, b->cdr, flags);    if(ret && (a->type != T_VOID))
pike.git/src/pike_types.c:3480:    a = a->cdr;    goto recurse;    }    }       case PIKE_T_RING:    a = a->car;    goto recurse;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    a = a->cdr;    goto recurse;       case T_NOT:    if (b->type == T_NOT) {    struct pike_type *tmp = a->car;    a = b->car;    b = tmp;    array_cnt = -array_cnt;    flags ^= LE_A_B_SWAPPED;
pike.git/src/pike_types.c:3600:    ret=low_pike_types_le(a, b->car, array_cnt, flags);    if (ret) return ret;    b = b->cdr;    goto recurse;       case PIKE_T_RING:    b = b->car;    goto recurse;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    b = b->cdr;    goto recurse;       case T_NOT:    /* Some common cases. */    switch(b->car->type) {    case T_NOT:    b = b->car->car;    goto recurse;    case T_MIXED:
pike.git/src/pike_types.c:4079:    type_stack_pop_to_mark();    if(!tmp) return 0;    return low_get_return_type(a->cdr, b);       case PIKE_T_RING:    return low_get_return_type(a->car, b);       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;    push_type(T_ARRAY);    return 1;    }       a = low_match_types(a, b, NO_SHORTCUTS);    if(a)    {
pike.git/src/pike_types.c:4160:    struct program *p;       switch(low_check_indexing(t, index_type, n))    {    case 0: return 0;    case -1:    add_ref(zero_type_string);    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;    }       switch(t->type)    {    case T_OBJECT:    {    p = id_to_program(CDR_TO_INT(t));       comefrom_int_index:
pike.git/src/pike_types.c:4346:    *    * FIXME: Is the above fixme valid for this function too?    */   static struct pike_type *debug_low_range_type(struct pike_type *t,    struct pike_type *index1_type,    struct pike_type *index2_type)   {    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;    }       switch(t->type)    {    case T_OBJECT:    {    p = id_to_program(CDR_TO_INT(t));       if(p)
pike.git/src/pike_types.c:4643:    free_type(a);    free_type(b);    return pop_unfinished_type();    }       case T_AND:    /* FIXME: Shouldn't this look at both branches? */    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:    case T_STRING: /* always int */    add_ref(int_type_string);    return int_type_string;       case T_MAPPING:    case T_MULTISET:    copy_pike_type(t, t->car);
pike.git/src/pike_types.c:4688:    low_check_indexing(type->cdr, index_type, n);       case T_AND:    return low_check_indexing(type->car, index_type, n) &&    low_check_indexing(type->cdr, index_type, n);       case T_NOT:    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:    if(low_match_types(string_type_string, index_type, 0) &&    low_check_indexing(type->car, index_type, n))    return 1;       case T_STRING:    return !!low_match_types(int_type_string, index_type, 0);   
pike.git/src/pike_types.c:4769:       case T_AND:    num = low_count_arguments(q->car);    num2 = low_count_arguments(q->cdr);    if(num<0 && num2>0) return num2;    if(num2<0 && num>0) return num;    if(num2<0 && num<0) return ~num<~num2?num:num2;    return num<num2?num:num2;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    return low_count_arguments(q->cdr);       default: return 0x7fffffff;       case T_FUNCTION:    while(q->type == T_FUNCTION)    {    num++;    q = q->cdr;    }
pike.git/src/pike_types.c:4813:    switch(q->type)    {    case T_OR:    case T_AND:    return MAXIMUM(low_count_arguments(q->car),    low_count_arguments(q->cdr));       default: return 0;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    return low_minimum_arguments(q->cdr);       case T_FUNCTION:    num = 0;    while(q->type == T_FUNCTION)    {    if(low_match_types(void_type_string, q->car, B_EXACT))    return num;       num++;
pike.git/src/pike_types.c:4972:    case T_AND:    res = and_pike_types(tmp = soft_cast(soft_type->car, orig_type, flags),    tmp2 = soft_cast(soft_type->cdr, orig_type, flags));    break;    case T_SCOPE:    case T_ASSIGN:    case PIKE_T_NAME:    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);    } else if (orig_type->type == T_VOID) {    copy_pike_type(res, zero_type_string);    } else {    copy_pike_type(res, orig_type);    }    break;    case T_ZERO:
pike.git/src/pike_types.c:5006:    break;    case T_AND:    res = and_pike_types(tmp = soft_cast(soft_type, orig_type->car, flags),    tmp2 = soft_cast(soft_type, orig_type->cdr, flags));    break;    case T_SCOPE:    case T_ASSIGN:    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);    } else {    copy_pike_type(res, soft_type);    }    break;    case T_VOID:    case T_ZERO:    if (flags & SOFT_WEAKER) {
pike.git/src/pike_types.c:5264:    }    break;    }    }    if (tmp) free_type(tmp);    if (tmp2) free_type(tmp2);    if (tmp3) free_type(tmp3);    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.    *    * The first argument has no OR or AND nodes.    *    * Returns NULL on failure.    *    * Returns continuation function type on success.    */   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   #else   #define CHECK_CALL_ARGS   #endif /* PIKE_TYPE_DEBUG */    )   {    struct pike_type *res = NULL;    struct pike_type *tmp;    struct pike_type *tmp2;    INT32 array_cnt = 0;      #ifdef PIKE_DEBUG    if (l_flag>2) {    fprintf(stderr, "%*slower_new_check_call(", indent*2, "");    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 */       loop:    /* Count the number of array levels. */    while(fun_type->type == PIKE_T_ARRAY) {    array_cnt++;    fun_type = fun_type->car;    }       switch(fun_type->type) {    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);    free_type(tmp2);    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;    break;    }    if ((res->type == T_NOT) || (tmp->type == T_NOT)) {    /* Special cases for NOT, since and_pike_types()    * doesn't seem to handle it reliably.    */    type_stack_mark();
pike.git/src/pike_types.c:5373:    /* Both sides are inverted. Pop both inversions. */    arg_type = arg_type->car;    fun_type = fun_type->car;    goto loop;    } else {    /* Move the inversion to the argument type. */    type_stack_mark();    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. */    if (res->type == T_NOT) {    tmp = res->car;    free_type(res);    res = tmp;    } else {    type_stack_mark();    if ((res->type == T_MANY) &&
pike.git/src/pike_types.c:5428:    * Program does not have a create(). function(:obj)    */       /* No create() -- No arguments. */    /* return NULL; */    copy_pike_type(res, mixed_type_string);    break;    } 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;       case PIKE_T_OBJECT:    fun_type = low_object_lfun_type(fun_type, LFUN_CALL);    if (fun_type) goto loop;       /* FIXME: Multiple cases:    * Untyped object. mixed    * Failed to lookup program id. mixed
pike.git/src/pike_types.c:5482: Inside #if defined(PIKE_DEBUG)
   * so that markers get set and kept. */   #ifdef PIKE_DEBUG    if (l_flag>2) {    fprintf(stderr, "%*sChecking argument type ", indent*2+2, "");    simple_describe_type(arg_type);    fprintf(stderr, " against function type ");    simple_describe_type(fun_type);    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) {    fprintf(stderr, "%*sNo match.\n", indent*2+2, "");    }   #endif /* PIKE_DEBUG */    res = NULL;    if (tmp) free_type(tmp);    break;    }
pike.git/src/pike_types.c:5512: Inside #if defined(PIKE_DEBUG)
  #ifdef PIKE_DEBUG    if (l_flag>2) {    fprintf(stderr, "%*sMore arguments required.\n", indent*2+2, "");    }   #endif /* PIKE_DEBUG */    res = NULL;    if (tmp) free_type(tmp);    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. */    break;    }    if (!array_cnt || !res) {   #ifdef PIKE_DEBUG    if (l_flag>2) {    if (res) {    fprintf(stderr, "%*s==> ", indent*2, "");
pike.git/src/pike_types.c:5563: Inside #if defined(PIKE_DEBUG)
   fprintf(stderr, "\n");    }   #endif /* PIKE_DEBUG */       return res;   }      /* 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;    struct pike_type *res;       /* FIXME: In strict mode we need to differentiate between    * two different kinds of OR:    * * Complex types, eg    * function(int:int)|function(float:float)    * or
pike.git/src/pike_types.c:5605:    clear_markers();    /* First split the argument type into basic types. */    switch(arg_type->type) {    case PIKE_T_SCOPE:    case T_ASSIGN:    case PIKE_T_NAME:    arg_type = arg_type->cdr;    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;    }    return tmp;    }    res = or_pike_types(tmp, tmp2, 1);    free_type(tmp);    free_type(tmp2);    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;    }    res = and_pike_types(tmp, tmp2);    free_type(tmp);    free_type(tmp2);    return res;       case T_VOID:    if (!(flags & CALL_7_6)) {    /* Promote void arguments to zero. */    arg_type = zero_type_string;    }    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    ))) {    return NULL;    }    return tmp;   }      /* Return the return type for the function type fun_type, if
pike.git/src/pike_types.c:5688:    /* Count the number of array levels. */    while(fun_type->type == PIKE_T_ARRAY) {    array_cnt++;    fun_type = fun_type->car;    }       switch(fun_type->type) {    case PIKE_T_SCOPE:    case T_ASSIGN:    case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    fun_type = fun_type->cdr;    goto loop;       case PIKE_T_RING:    fun_type = fun_type->car;    goto loop;       case T_OR:    if (!(res = new_get_return_type(fun_type->car, flags))) {    fun_type = fun_type->cdr;
pike.git/src/pike_types.c:5939:    loop:    /* Get rid of the array levels. */    while(fun_type->type == PIKE_T_ARRAY) {    fun_type = fun_type->car;    }       switch(fun_type->type) {    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;       case T_OR:    if (!(res = get_first_arg_type(fun_type->car, flags))) {    fun_type = fun_type->cdr;    goto loop;    }    if (!(tmp = get_first_arg_type(fun_type->cdr, flags))) {
pike.git/src/pike_types.c:5964:    break;    case T_AND:    if (!(res = get_first_arg_type(fun_type->car, flags))) {    break;    }    if (!(tmp = get_first_arg_type(fun_type->cdr, flags))) {    free_type(res);    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;    case T_NOT:    if (!(res = get_first_arg_type(fun_type->car, flags))) {    break;    }    if (res->type == T_NOT) {    copy_pike_type(tmp, res->car);    free_type(res);
pike.git/src/pike_types.c:6045:    return res;   }      /* NOTE: fun_type loses a reference. */   struct pike_type *new_check_call(struct pike_string *fun_name,    struct pike_type *fun_type,    node *args, INT32 *argno)   {    struct pike_type *tmp = NULL;    struct pike_type *res = NULL; +  struct svalue *sval = NULL;    int flags = 0;       debug_malloc_touch(fun_type);       while (args && (args->token == F_ARG_LIST) && fun_type) {    fun_type = new_check_call(fun_name, fun_type, CAR(args), argno);    debug_malloc_touch(fun_type);    args = CDR(args);    }       if (!args || !fun_type) {    debug_malloc_touch(fun_type);    return fun_type;    }       (*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);    }   #endif /* PIKE_DEBUG */       if (TEST_COMPAT(7, 6)) {    /* Attempt to reduce strictness to Pike 7.6 levels. */    flags |= CALL_7_6;    }   
pike.git/src/pike_types.c:6092: Inside #if defined(PIKE_DEBUG)
   if (l_flag>2) {    fprintf(stderr, "\n The argument is a splice operator.\n");    }   #endif /* PIKE_DEBUG */       copy_pike_type(res, fun_type);       /* 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    if (l_flag>4) {    fprintf(stderr, "\n sub_result_type: ");    simple_describe_type(fun_type);    }   #endif /* PIKE_DEBUG */       res = dmalloc_touch(struct pike_type *,
pike.git/src/pike_types.c:6145: Inside #if defined(PIKE_DEBUG)
     #ifdef PIKE_DEBUG    if (l_flag>2) {    fprintf(stderr, "\n result: ");    simple_describe_type(res);    fprintf(stderr, " OK.\n");    }   #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) {    fprintf(stderr, " OK.\n");    }   #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))) {    yytype_error(NULL, tmp, args->type, YYTE_IS_WARNING);    free_type(tmp);    } else {    yytype_error(NULL, NULL, args->type, YYTE_IS_WARNING);    }    } else {    free_type(tmp);
pike.git/src/pike_types.c:6186: Inside #if defined(PIKE_DEBUG)
   fprintf(stderr, " Bad argument.\n");    }   #endif /* PIKE_DEBUG */    my_yyerror("Bad argument %d to %S.",    *argno, fun_name);    yytype_error(NULL, tmp, args->type, 0);       /* 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);   #ifdef PIKE_DEBUG    if (l_flag>2) {    fprintf(stderr, " Created continuation type: ");    simple_describe_type(tmp2);    fprintf(stderr, " OK.\n");    }   #endif /* PIKE_DEBUG */
pike.git/src/pike_types.c:6261:    }    return pop_unfinished_type();    }       case T_ARRAY:    return zzap_function_return(a->car, id);       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: */    type_stack_mark();    push_object_type(1, id);    push_type(T_VOID);    push_type(T_MIXED);    push_type(T_OR);    push_type(T_MANY);    return pop_unfinished_type();
pike.git/src/pike_types.c:6521:       case T_FUNCTION:    case T_MANY:    case T_ARRAY:    /* might want to check for `() */       default:    return 0;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    return type_may_overload(type->cdr, lfun);       case PIKE_T_RING:    return type_may_overload(type->car, lfun);       case T_OR:    return type_may_overload(type->car, lfun) ||    type_may_overload(type->cdr, lfun);       case T_AND:
pike.git/src/pike_types.c:6715:    *cont = type_string + 1;    push_int_type((INT32)-0x80000000, 0x7fffffff);    break;       case T_OBJECT:    *cont = type_string + 6; /* 1 + sizeof(INT32) + 1 */    push_object_type(type_string[1], extract_type_int((char *)type_string+2));    break;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    {    int size_shift = type_string[1] & 0x3;    struct pike_string *str;    INT32 bytes;    /* bit 0 & 1: size_shift    * bit 2 ==> little endian.    *    * The loops check the lsb first, since it's most likely to    * be non-zero.    */
pike.git/src/pike_types.c:6787:    tmp = str->str[len];    str->str[len] = str->str[len+3];    str->str[len+3] = tmp;    tmp = str->str[len+1];    str->str[len+1] = str->str[len+2];    str->str[len+2] = tmp;    }    }    }    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;    }    default:    Pike_fatal("compile_type_string(): Error in type string %d.\n", type);    /* NOT_REACHED */    break;    }   }   
pike.git/src/pike_types.c:6826:    case T_NOT:    return !pike_type_allow_premature_toss(type->car);       case T_OBJECT:    case T_MIXED:    case T_FUNCTION:    case T_MANY:    return 0;       case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    case T_SCOPE:    case T_ASSIGN:    type = type->cdr;    goto again;       case PIKE_T_RING:    type = type->car;    goto again;       case T_OR:
pike.git/src/pike_types.c:6879:    recurse:    switch(t->type) {    case T_ARRAY:    case T_MULTISET:    case T_TYPE:    case T_NOT:    case T_PROGRAM:    my_putchar(t->type);    /* FALL_THROUGH */    case PIKE_T_NAME: +  case PIKE_T_ATTRIBUTE:    t = t->car;    goto recurse;       case PIKE_T_RING:    case T_TUPLE:    case T_MAPPING:    case T_OR:    case T_AND:    my_putchar(t->type);    low_type_to_string(t->car);
pike.git/src/pike_types.c:7032: Inside #if 0 and #if defined(DEBUG_MALLOC)
   gc_mark_external(any_type_string, " as any_type_string");    if (weak_type_string)    gc_mark_external(weak_type_string, " as weak_type_string");    } GC_LEAVE;   }      static struct callback *dmalloc_gc_callback = NULL;   #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. */    pike_type_hash = (struct pike_type **)xalloc(sizeof(struct pike_type *) *    PIKE_TYPE_HASH_SIZE);    MEMSET(pike_type_hash, 0, sizeof(struct pike_type *) * PIKE_TYPE_HASH_SIZE);    pike_type_hash_size = PIKE_TYPE_HASH_SIZE;    init_pike_type_blocks();       string0_type_string = CONSTTYPE(tStr0);
pike.git/src/pike_types.c:7060:    mapping_type_string = CONSTTYPE(tMapping);    function_type_string = CONSTTYPE(tFunction);    type_type_string = CONSTTYPE(tType(tMix));    void_type_string = CONSTTYPE(tVoid);    zero_type_string = CONSTTYPE(tZero);    any_type_string = CONSTTYPE(tOr(tVoid,tMix));    weak_type_string = CONSTTYPE(tOr4(tArray,tMultiset,tMapping,    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);   #endif /* DEBUG_MALLOC */   #endif /* 0 */   }      void cleanup_pike_types(void)   {   #ifdef DEBUG_MALLOC    struct pike_type_location *t = all_pike_type_locations;       while(t) {    free_type(t->t);    t = t->next;    }   #endif /* DEBUG_MALLOC */    -  +  if (builtin_attributes) { +  free_mapping(builtin_attributes); +  builtin_attributes = NULL; +  } +     clear_markers();       free_type(string0_type_string);    string0_type_string = NULL;    free_type(string_type_string);    string_type_string = NULL;    free_type(int_type_string);    int_type_string = NULL;    free_type(float_type_string);    float_type_string = NULL;
pike.git/src/pike_types.c:7153: Inside #if 0 and #if defined(PIKE_DEBUG)
  #ifdef PIKE_DEBUG      void gc_mark_type_as_referenced(struct pike_type *t)   {    if (gc_mark(t)) {    GC_ENTER(t, PIKE_T_TYPE) {    switch(t->type) {    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:    case T_MANY:    case PIKE_T_RING:    case PIKE_T_TUPLE:    case PIKE_T_MAPPING:    case T_OR:    case T_AND:    if (t->cdr) gc_mark_type_as_referenced(t->cdr);
pike.git/src/pike_types.c:7246: Inside #if 0 and #if defined(PIKE_DEBUG)
   return unreferenced;   }      void real_gc_cycle_check_type(struct pike_type *t, int weak)   {    GC_CYCLE_ENTER(t, PIKE_T_TYPE, weak) {    switch(t->type) {    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:    case T_MANY:    case PIKE_T_RING:    case PIKE_T_TUPLE:    case PIKE_T_MAPPING:    case T_OR:    case T_AND:    if (t->cdr) gc_cycle_check_type(t->cdr, 0);