To: vim_dev@googlegroups.com Subject: Patch 8.2.1691 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1691 Problem: Vim9: list is not accepted where list is expected. Solution: Add functions to allocate and free a type_T, use it in ISN_CHECKTYPE. (closes #6959) Files: src/vim9.h, src/globals.h, src/vim9compile.c, src/vim9execute.c, src/vim9type.c, src/proto/vim9type.pro, src/errors.h, src/evalfunc.c, src/testdir/test_vim9_disassemble.vim, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_script.vim *** ../vim-8.2.1690/src/vim9.h 2020-09-14 21:38:47.756976285 +0200 --- src/vim9.h 2020-09-16 12:28:45.638357945 +0200 *************** *** 207,213 **** // arguments to ISN_CHECKTYPE typedef struct { ! vartype_T ct_type; int ct_off; // offset in stack, -1 is bottom } checktype_T; --- 207,213 ---- // arguments to ISN_CHECKTYPE typedef struct { ! type_T *ct_type; int ct_off; // offset in stack, -1 is bottom } checktype_T; *** ../vim-8.2.1690/src/globals.h 2020-09-06 21:47:39.323041533 +0200 --- src/globals.h 2020-09-16 12:17:44.684511692 +0200 *************** *** 391,432 **** // Commonly used types. ! EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL); ! ! EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, 0, &t_unknown, NULL); ! EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL); ! EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL); ! EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL); ! EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL); ! EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL); ! EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL); ! ! EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_unknown, NULL); ! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_unknown, NULL); ! ! EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL); ! EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL); ! EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL); ! ! EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL); ! EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL); ! #endif --- 391,431 ---- // Commonly used types. ! EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL); ! EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL); ! ! EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL); ! EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL); ! EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); ! EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL); ! EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL); ! EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL); ! EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL); ! EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL); ! EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, NULL); ! ! EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL); ! EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL); ! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, NULL); ! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL); ! ! EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL); ! EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL); ! EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL); ! EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL); ! ! EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL); ! EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL); ! EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL); #endif *** ../vim-8.2.1690/src/vim9compile.c 2020-09-14 22:39:05.969192788 +0200 --- src/vim9compile.c 2020-09-16 13:49:48.699234854 +0200 *************** *** 704,710 **** } static int ! generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; --- 704,713 ---- } static int ! generate_TYPECHECK( ! cctx_T *cctx, ! type_T *expected, ! int offset) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; *************** *** 712,730 **** RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL) return FAIL; ! // TODO: whole type, e.g. for a function also arg and return types ! isn->isn_arg.type.ct_type = vartype->tt_type; isn->isn_arg.type.ct_off = offset; ! // type becomes vartype ! ((type_T **)stack->ga_data)[stack->ga_len + offset] = vartype; return OK; } /* * Check that ! * - "actual" is "expected" type or * - "actual" is a type that can be "expected" type: add a runtime check; or * - return FAIL. */ --- 715,732 ---- RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL) return FAIL; ! isn->isn_arg.type.ct_type = alloc_type(expected); isn->isn_arg.type.ct_off = offset; ! // type becomes expected ! ((type_T **)stack->ga_data)[stack->ga_len + offset] = expected; return OK; } /* * Check that ! * - "actual" matches "expected" type or * - "actual" is a type that can be "expected" type: add a runtime check; or * - return FAIL. */ *************** *** 747,763 **** if (check_type(expected, actual, FALSE, 0) == OK) return OK; ! if (actual->tt_type != VAR_ANY ! && actual->tt_type != VAR_UNKNOWN ! && !(actual->tt_type == VAR_FUNC ! && (actual->tt_member == &t_any || actual->tt_argcount < 0))) { ! if (!silent) ! type_mismatch(expected, actual); ! return FAIL; } ! generate_TYPECHECK(cctx, expected, offset); ! return OK; } /* --- 749,777 ---- if (check_type(expected, actual, FALSE, 0) == OK) return OK; ! ! // If the actual type can be the expected type add a runtime check. ! // TODO: if it's a constant a runtime check makes no sense. ! if (actual->tt_type == VAR_ANY ! || actual->tt_type == VAR_UNKNOWN ! || (actual->tt_type == VAR_FUNC ! && (expected->tt_type == VAR_FUNC ! || expected->tt_type == VAR_PARTIAL) ! && (actual->tt_member == &t_any || actual->tt_argcount < 0)) ! || (actual->tt_type == VAR_LIST ! && expected->tt_type == VAR_LIST ! && actual->tt_member == &t_any) ! || (actual->tt_type == VAR_DICT ! && expected->tt_type == VAR_DICT ! && actual->tt_member == &t_any)) { ! generate_TYPECHECK(cctx, expected, offset); ! return OK; } ! ! if (!silent) ! type_mismatch(expected, actual); ! return FAIL; } /* *************** *** 776,782 **** if (number == 0 || number == 1) { ! type_T *type = alloc_type(cctx->ctx_type_list); // A 0 or 1 number can also be used as a bool. if (type != NULL) --- 790,796 ---- if (number == 0 || number == 1) { ! type_T *type = get_type_ptr(cctx->ctx_type_list); // A 0 or 1 number can also be used as a bool. if (type != NULL) *************** *** 4037,4043 **** typep = ((type_T **)stack->ga_data) + stack->ga_len - 1; if (*typep != &t_bool) { ! type_T *type = alloc_type(cctx->ctx_type_list); if (type != NULL) { --- 4051,4057 ---- typep = ((type_T **)stack->ga_data) + stack->ga_len - 1; if (*typep != &t_bool) { ! type_T *type = get_type_ptr(cctx->ctx_type_list); if (type != NULL) { *************** *** 7290,7295 **** --- 7304,7313 ---- } break; + case ISN_CHECKTYPE: + free_type(isn->isn_arg.type.ct_type); + break; + case ISN_2BOOL: case ISN_2STRING: case ISN_2STRING_ANY: *************** *** 7301,7307 **** case ISN_CATCH: case ISN_CHECKLEN: case ISN_CHECKNR: - case ISN_CHECKTYPE: case ISN_COMPAREANY: case ISN_COMPAREBLOB: case ISN_COMPAREBOOL: --- 7319,7324 ---- *** ../vim-8.2.1690/src/vim9execute.c 2020-09-14 21:38:47.756976285 +0200 --- src/vim9execute.c 2020-09-16 13:57:42.669334121 +0200 *************** *** 2535,2564 **** checktype_T *ct = &iptr->isn_arg.type; tv = STACK_TV_BOT(ct->ct_off); ! // TODO: better type comparison ! if (tv->v_type != ct->ct_type ! && !((tv->v_type == VAR_PARTIAL ! && ct->ct_type == VAR_FUNC) ! || (tv->v_type == VAR_FUNC ! && ct->ct_type == VAR_PARTIAL))) { ! if (tv->v_type == VAR_NUMBER && ct->ct_type == VAR_BOOL ! && (tv->vval.v_number == 0 ! || tv->vval.v_number == 1)) ! { ! // number 0 is FALSE, number 1 is TRUE ! tv->v_type = VAR_BOOL; ! tv->vval.v_number = tv->vval.v_number ? VVAL_TRUE : VVAL_FALSE; - } - else - { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_expected_str_but_got_str), - vartype_name(ct->ct_type), - vartype_name(tv->v_type)); - goto on_error; - } } } break; --- 2535,2553 ---- checktype_T *ct = &iptr->isn_arg.type; tv = STACK_TV_BOT(ct->ct_off); ! SOURCING_LNUM = iptr->isn_lnum; ! if (check_typval_type(ct->ct_type, tv, 0) == FAIL) ! goto on_error; ! ! // number 0 is FALSE, number 1 is TRUE ! if (tv->v_type == VAR_NUMBER ! && ct->ct_type->tt_type == VAR_BOOL ! && (tv->vval.v_number == 0 ! || tv->vval.v_number == 1)) { ! tv->v_type = VAR_BOOL; ! tv->vval.v_number = tv->vval.v_number ? VVAL_TRUE : VVAL_FALSE; } } break; *************** *** 3286,3295 **** case ISN_NEGATENR: smsg("%4d NEGATENR", current); break; case ISN_CHECKNR: smsg("%4d CHECKNR", current); break; ! case ISN_CHECKTYPE: smsg("%4d CHECKTYPE %s stack[%d]", current, ! vartype_name(iptr->isn_arg.type.ct_type), ! iptr->isn_arg.type.ct_off); ! break; case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current, iptr->isn_arg.checklen.cl_more_OK ? ">= " : "", iptr->isn_arg.checklen.cl_min_len); --- 3275,3290 ---- case ISN_NEGATENR: smsg("%4d NEGATENR", current); break; case ISN_CHECKNR: smsg("%4d CHECKNR", current); break; ! case ISN_CHECKTYPE: ! { ! char *tofree; ! ! smsg("%4d CHECKTYPE %s stack[%d]", current, ! type_name(iptr->isn_arg.type.ct_type, &tofree), ! iptr->isn_arg.type.ct_off); ! vim_free(tofree); ! break; ! } case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current, iptr->isn_arg.checklen.cl_more_OK ? ">= " : "", iptr->isn_arg.checklen.cl_min_len); *** ../vim-8.2.1690/src/vim9type.c 2020-09-09 22:27:55.425537078 +0200 --- src/vim9type.c 2020-09-16 15:19:29.363276116 +0200 *************** *** 22,31 **** /* * Allocate memory for a type_T and add the pointer to type_gap, so that it can ! * be freed later. */ type_T * ! alloc_type(garray_T *type_gap) { type_T *type; --- 22,31 ---- /* * Allocate memory for a type_T and add the pointer to type_gap, so that it can ! * be easily freed later. */ type_T * ! get_type_ptr(garray_T *type_gap) { type_T *type; *************** *** 48,53 **** --- 48,107 ---- ga_clear(gap); } + /* + * Take a type that is using entries in a growarray and turn it into a type + * with allocated entries. + */ + type_T * + alloc_type(type_T *type) + { + type_T *ret; + + if (type == NULL) + return NULL; + + // A fixed type never contains allocated types, return as-is. + if (type->tt_flags & TTFLAG_STATIC) + return type; + + ret = ALLOC_ONE(type_T); + *ret = *type; + + if (ret->tt_member != NULL) + ret->tt_member = alloc_type(ret->tt_member); + if (type->tt_args != NULL) + { + int i; + + ret->tt_args = ALLOC_MULT(type_T *, type->tt_argcount); + if (ret->tt_args != NULL) + for (i = 0; i < type->tt_argcount; ++i) + ret->tt_args[i] = alloc_type(type->tt_args[i]); + } + + return ret; + } + + /* + * Free a type that was created with alloc_type(). + */ + void + free_type(type_T *type) + { + int i; + + if (type == NULL || (type->tt_flags & TTFLAG_STATIC)) + return; + if (type->tt_args != NULL) + { + for (i = 0; i < type->tt_argcount; ++i) + free_type(type->tt_args[i]); + vim_free(type->tt_args); + } + free_type(type->tt_member); + vim_free(type); + } + type_T * get_list_type(type_T *member_type, garray_T *type_gap) { *************** *** 67,73 **** return &t_list_string; // Not a common type, create a new entry. ! type = alloc_type(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_LIST; --- 121,127 ---- return &t_list_string; // Not a common type, create a new entry. ! type = get_type_ptr(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_LIST; *************** *** 96,102 **** return &t_dict_string; // Not a common type, create a new entry. ! type = alloc_type(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_DICT; --- 150,156 ---- return &t_dict_string; // Not a common type, create a new entry. ! type = get_type_ptr(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_DICT; *************** *** 112,118 **** type_T * alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap) { ! type_T *type = alloc_type(type_gap); if (type == NULL) return &t_any; --- 166,172 ---- type_T * alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap) { ! type_T *type = get_type_ptr(type_gap); if (type == NULL) return &t_any; *************** *** 197,209 **** /* * Get a type_T for a typval_T. ! * "type_list" is used to temporarily create types in. */ static type_T * typval2type_int(typval_T *tv, garray_T *type_gap) { type_T *type; ! type_T *member_type; if (tv->v_type == VAR_NUMBER) return &t_number; --- 251,264 ---- /* * Get a type_T for a typval_T. ! * "type_gap" is used to temporarily create types in. */ static type_T * typval2type_int(typval_T *tv, garray_T *type_gap) { type_T *type; ! type_T *member_type = &t_any; ! int argcount = 0; if (tv->v_type == VAR_NUMBER) return &t_number; *************** *** 262,269 **** else name = tv->vval.v_string; if (name != NULL) ! // TODO: how about a builtin function? ! ufunc = find_func(name, FALSE, NULL); if (ufunc != NULL) { // May need to get the argument types from default values by --- 317,334 ---- else name = tv->vval.v_string; if (name != NULL) ! { ! int idx = find_internal_func(name); ! ! if (idx >= 0) ! { ! // TODO: get actual arg count and types ! argcount = -1; ! member_type = internal_func_ret_type(idx, 0, NULL); ! } ! else ! ufunc = find_func(name, FALSE, NULL); ! } if (ufunc != NULL) { // May need to get the argument types from default values by *************** *** 276,286 **** } } ! type = alloc_type(type_gap); if (type == NULL) return NULL; type->tt_type = tv->v_type; ! type->tt_member = &t_any; return type; } --- 341,352 ---- } } ! type = get_type_ptr(type_gap); if (type == NULL) return NULL; type->tt_type = tv->v_type; ! type->tt_argcount = argcount; ! type->tt_member = member_type; return type; } *************** *** 311,317 **** && (tv->vval.v_number == 0 || tv->vval.v_number == 1)) || (tv->v_lock & VAR_BOOL_OK))) { ! type_T *newtype = alloc_type(type_gap); // Number 0 and 1 and expression with "&&" or "||" can also be used // for bool. --- 377,383 ---- && (tv->vval.v_number == 0 || tv->vval.v_number == 1)) || (tv->v_lock & VAR_BOOL_OK))) { ! type_T *newtype = get_type_ptr(type_gap); // Number 0 and 1 and expression with "&&" or "||" can also be used // for bool. *************** *** 420,425 **** --- 486,492 ---- ret = check_type(expected->tt_member, actual->tt_member, FALSE, 0); if (ret == OK && expected->tt_argcount != -1 + && actual->tt_argcount != -1 && (actual->tt_argcount < expected->tt_min_argcount || actual->tt_argcount > expected->tt_argcount)) ret = FAIL; *************** *** 940,946 **** ga_init2(&ga, 1, 100); if (ga_grow(&ga, 20) == FAIL) return "[unknown]"; - *tofree = ga.ga_data; STRCPY(ga.ga_data, "func("); ga.ga_len += 5; --- 1007,1012 ---- *************** *** 963,982 **** if (ga_grow(&ga, len + 8) == FAIL) { vim_free(arg_free); return "[unknown]"; } - *tofree = ga.ga_data; if (varargs && i == type->tt_argcount - 1) ! { ! STRCPY((char *)ga.ga_data + ga.ga_len, "..."); ! ga.ga_len += 3; ! } else if (i >= type->tt_min_argcount) *((char *)ga.ga_data + ga.ga_len++) = '?'; ! STRCPY((char *)ga.ga_data + ga.ga_len, arg_type); ! ga.ga_len += len; vim_free(arg_free); } if (type->tt_member == &t_void) STRCPY((char *)ga.ga_data + ga.ga_len, ")"); --- 1029,1047 ---- if (ga_grow(&ga, len + 8) == FAIL) { vim_free(arg_free); + ga_clear(&ga); return "[unknown]"; } if (varargs && i == type->tt_argcount - 1) ! ga_concat(&ga, (char_u *)"..."); else if (i >= type->tt_min_argcount) *((char *)ga.ga_data + ga.ga_len++) = '?'; ! ga_concat(&ga, (char_u *)arg_type); vim_free(arg_free); } + if (type->tt_argcount < 0) + // any number of arguments + ga_concat(&ga, (char_u *)"..."); if (type->tt_member == &t_void) STRCPY((char *)ga.ga_data + ga.ga_len, ")"); *************** *** 990,1007 **** if (ga_grow(&ga, len) == FAIL) { vim_free(ret_free); return "[unknown]"; } - *tofree = ga.ga_data; STRCPY((char *)ga.ga_data + ga.ga_len, "): "); STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name); vim_free(ret_free); } return ga.ga_data; } return name; } - #endif // FEAT_EVAL --- 1055,1072 ---- if (ga_grow(&ga, len) == FAIL) { vim_free(ret_free); + ga_clear(&ga); return "[unknown]"; } STRCPY((char *)ga.ga_data + ga.ga_len, "): "); STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name); vim_free(ret_free); } + *tofree = ga.ga_data; return ga.ga_data; } return name; } #endif // FEAT_EVAL *** ../vim-8.2.1690/src/proto/vim9type.pro 2020-09-09 22:27:55.425537078 +0200 --- src/proto/vim9type.pro 2020-09-16 13:02:56.784516124 +0200 *************** *** 1,6 **** /* vim9type.c */ ! type_T *alloc_type(garray_T *type_gap); void clear_type_list(garray_T *gap); type_T *get_list_type(type_T *member_type, garray_T *type_gap); type_T *get_dict_type(type_T *member_type, garray_T *type_gap); type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap); --- 1,8 ---- /* vim9type.c */ ! type_T *get_type_ptr(garray_T *type_gap); void clear_type_list(garray_T *gap); + type_T *alloc_type(type_T *type); + void free_type(type_T *type); type_T *get_list_type(type_T *member_type, garray_T *type_gap); type_T *get_dict_type(type_T *member_type, garray_T *type_gap); type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap); *** ../vim-8.2.1690/src/errors.h 2020-09-14 21:38:47.756976285 +0200 --- src/errors.h 2020-09-16 12:42:28.068175429 +0200 *************** *** 52,58 **** EXTERN char e_name_too_long_str[] INIT(= N_("E1011: name too long: %s")); EXTERN char e_type_mismatch_expected_str_but_got_str[] ! INIT(= N_("E1012: type mismatch, expected %s but got %s")); EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[] INIT(= N_("E1013: argument %d: type mismatch, expected %s but got %s")); EXTERN char e_invalid_key_str[] --- 52,58 ---- EXTERN char e_name_too_long_str[] INIT(= N_("E1011: name too long: %s")); EXTERN char e_type_mismatch_expected_str_but_got_str[] ! INIT(= N_("E1012: Type mismatch; expected %s but got %s")); EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[] INIT(= N_("E1013: argument %d: type mismatch, expected %s but got %s")); EXTERN char e_invalid_key_str[] *** ../vim-8.2.1690/src/evalfunc.c 2020-09-11 22:25:11.298775020 +0200 --- src/evalfunc.c 2020-09-16 15:04:27.991180534 +0200 *************** *** 386,396 **** static type_T * ret_remove(int argcount UNUSED, type_T **argtypes) { ! if (argtypes[0]->tt_type == VAR_LIST ! || argtypes[0]->tt_type == VAR_DICT) ! return argtypes[0]->tt_member; ! if (argtypes[0]->tt_type == VAR_BLOB) ! return &t_number; return &t_any; } --- 386,399 ---- static type_T * ret_remove(int argcount UNUSED, type_T **argtypes) { ! if (argtypes != NULL) ! { ! if (argtypes[0]->tt_type == VAR_LIST ! || argtypes[0]->tt_type == VAR_DICT) ! return argtypes[0]->tt_member; ! if (argtypes[0]->tt_type == VAR_BLOB) ! return &t_number; ! } return &t_any; } *************** *** 2915,2921 **** { if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING) return &t_func_any; ! return &t_func_void; } /* --- 2918,2924 ---- { if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING) return &t_func_any; ! return &t_func_unknown; } /* *** ../vim-8.2.1690/src/testdir/test_vim9_disassemble.vim 2020-09-09 20:03:42.908661678 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-09-16 13:08:31.918312336 +0200 *************** *** 258,264 **** '\d STORE $2\_s*' .. '\[x, y; l\] = g:stringlist\_s*' .. '\d LOADG g:stringlist\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d CHECKLEN >= 2\_s*' .. '\d\+ ITEM 0\_s*' .. '\d\+ CHECKTYPE string stack\[-1\]\_s*' .. --- 258,264 ---- '\d STORE $2\_s*' .. '\[x, y; l\] = g:stringlist\_s*' .. '\d LOADG g:stringlist\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d CHECKLEN >= 2\_s*' .. '\d\+ ITEM 0\_s*' .. '\d\+ CHECKTYPE string stack\[-1\]\_s*' .. *************** *** 829,835 **** '\d STORE -1 in $1\_s*' .. '\d PUSHS "\["one", "two"\]"\_s*' .. '\d BCALL eval(argc 1)\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d FOR $1 -> \d\+\_s*' .. '\d STORE $2\_s*' .. 'res ..= str\_s*' .. --- 829,835 ---- '\d STORE -1 in $1\_s*' .. '\d PUSHS "\["one", "two"\]"\_s*' .. '\d BCALL eval(argc 1)\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d FOR $1 -> \d\+\_s*' .. '\d STORE $2\_s*' .. 'res ..= str\_s*' .. *************** *** 1144,1150 **** '\d STORE $0\_s*' .. 'return res\_s*' .. '\d LOAD $0\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d RETURN', instr) assert_equal([2, 3, 4], AnySlice()) --- 1144,1150 ---- '\d STORE $0\_s*' .. 'return res\_s*' .. '\d LOAD $0\_s*' .. ! '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d RETURN', instr) assert_equal([2, 3, 4], AnySlice()) *** ../vim-8.2.1690/src/testdir/test_vim9_expr.vim 2020-09-14 16:50:01.751887481 +0200 --- src/testdir/test_vim9_expr.vim 2020-09-16 14:07:37.855022314 +0200 *************** *** 1438,1445 **** let old: list = v:oldfiles let compl: dict = v:completed_item ! CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: type mismatch, expected list but got list', 1) ! CheckDefFailure(["let old: dict = v:completed_item"], 'E1012: type mismatch, expected dict but got dict', 1) enddef def Test_expr7_special() --- 1438,1448 ---- let old: list = v:oldfiles let compl: dict = v:completed_item ! CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: Type mismatch; expected list but got list', 1) ! new ! exec "normal! afoo fo\\" ! CheckDefExecFailure(["let old: dict = v:completed_item"], 'E1012: Type mismatch; expected dict but got dict', 1) ! bwipe! enddef def Test_expr7_special() *************** *** 1520,1533 **** CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2) ! CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1) CheckDefFailure(["let x = g:list_mixed["], 'E1097:', 2) CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 2) CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:', 1) ! CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:', 1) ! CheckDefFailure(["let l: list = ['x', 234]"], 'E1012:', 1) ! CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:', 1) ! CheckDefFailure(["let l: list = ['x', 123]"], 'E1012:', 1) enddef def Test_expr7_list_vim9script() --- 1523,1536 ---- CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2) ! CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1012:', 1) CheckDefFailure(["let x = g:list_mixed["], 'E1097:', 2) CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 2) CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:', 1) ! CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) ! CheckDefExecFailure(["let l: list = ['x', 234]"], 'E1012:', 1) ! CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) ! CheckDefExecFailure(["let l: list = ['x', 123]"], 'E1012:', 1) enddef def Test_expr7_list_vim9script() *************** *** 1731,1740 **** CheckDefExecFailure(["let x = g:anint.member"], 'E715:', 1) CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:', 1) ! CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) ! CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) ! CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) ! CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) enddef def Test_expr7_dict_vim9script() --- 1734,1743 ---- CheckDefExecFailure(["let x = g:anint.member"], 'E715:', 1) CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:', 1) ! CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) ! CheckDefExecFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) ! CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) ! CheckDefExecFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) enddef def Test_expr7_dict_vim9script() *************** *** 1840,1846 **** CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:', 1) CheckDefExecFailure(["let d: dict", "echo d['a']"], 'E716:', 2) ! CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list', 2) enddef def Test_expr7_any_index_slice() --- 1843,1849 ---- CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:', 1) CheckDefExecFailure(["let d: dict", "echo d['a']"], 'E716:', 2) ! CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1012: Type mismatch; expected dict but got list', 2) enddef def Test_expr7_any_index_slice() *************** *** 2311,2317 **** CheckScriptSuccess(['vim9script'] + lines) lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] ! CheckDefExecFailure(lines, 'E1029:') CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3) enddef --- 2314,2320 ---- CheckScriptSuccess(['vim9script'] + lines) lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] ! CheckDefExecFailure(lines, 'E1012:') CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3) enddef *** ../vim-8.2.1690/src/testdir/test_vim9_func.vim 2020-09-14 17:04:27.652663882 +0200 --- src/testdir/test_vim9_func.vim 2020-09-16 15:06:45.854577887 +0200 *************** *** 30,36 **** def Test_return_something() ReturnString()->assert_equal('string') ReturnNumber()->assert_equal(123) ! assert_fails('ReturnGlobal()', 'E1029: Expected number but got string', '', 1, 'ReturnGlobal') enddef def Test_missing_return() --- 30,36 ---- def Test_return_something() ReturnString()->assert_equal('string') ReturnNumber()->assert_equal(123) ! assert_fails('ReturnGlobal()', 'E1012: Type mismatch; expected number but got string', '', 1, 'ReturnGlobal') enddef def Test_missing_return() *************** *** 487,493 **** enddef let Funcref: func(string) = function('UseNumber') END ! CheckScriptFailure(lines, 'E1012: type mismatch, expected func(string) but got func(number)') lines =<< trim END vim9script --- 487,493 ---- enddef let Funcref: func(string) = function('UseNumber') END ! CheckScriptFailure(lines, 'E1012: Type mismatch; expected func(string) but got func(number)') lines =<< trim END vim9script *************** *** 976,1012 **** let RefVoid: func: void RefVoid = FuncNoArgNoRet RefVoid = FuncOneArgNoRet ! CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number') ! CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: type mismatch, expected func() but got func(): string') let RefAny: func(): any RefAny = FuncNoArgRetNumber RefAny = FuncNoArgRetString ! CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): any but got func()') ! CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: type mismatch, expected func(): any but got func(number)') let RefNr: func: number RefNr = FuncNoArgRetNumber RefNr = FuncOneArgRetNumber ! CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): number but got func()') ! CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: type mismatch, expected func(): number but got func(): string') let RefStr: func: string RefStr = FuncNoArgRetString RefStr = FuncOneArgRetString ! CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): string but got func()') ! CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func(): string but got func(): number') enddef def Test_func_type_fails() CheckDefFailure(['let ref1: func()'], 'E704:') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: type mismatch, expected func() but got func(number)') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: type mismatch, expected func() but got func(number): number') ! CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(bool) but got func(bool, number)') ! CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(?bool) but got func(bool, number)') ! CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(...bool) but got func(bool, number)') CheckDefFailure(['let RefWrong: func(string ,number)'], 'E1068:') CheckDefFailure(['let RefWrong: func(string,number)'], 'E1069:') --- 976,1012 ---- let RefVoid: func: void RefVoid = FuncNoArgNoRet RefVoid = FuncOneArgNoRet ! CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...) but got func(): number') ! CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...) but got func(): string') let RefAny: func(): any RefAny = FuncNoArgRetNumber RefAny = FuncNoArgRetString ! CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func()') ! CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func(number)') let RefNr: func: number RefNr = FuncNoArgRetNumber RefNr = FuncOneArgRetNumber ! CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): number but got func()') ! CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...): number but got func(): string') let RefStr: func: string RefStr = FuncNoArgRetString RefStr = FuncOneArgRetString ! CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): string but got func()') ! CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...): string but got func(): number') enddef def Test_func_type_fails() CheckDefFailure(['let ref1: func()'], 'E704:') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(): number') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func() but got func(number)') ! CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(number): number') ! CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(bool) but got func(bool, number)') ! CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(?bool) but got func(bool, number)') ! CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(...bool) but got func(bool, number)') CheckDefFailure(['let RefWrong: func(string ,number)'], 'E1068:') CheckDefFailure(['let RefWrong: func(string,number)'], 'E1069:') *************** *** 1026,1032 **** str = FuncOneArgRetAny('yes') str->assert_equal('yes') ! CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: type mismatch, expected string but got number') enddef def MultiLine( --- 1026,1032 ---- str = FuncOneArgRetAny('yes') str->assert_equal('yes') ! CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: Type mismatch; expected string but got number') enddef def MultiLine( *************** *** 1204,1210 **** return join(Ref(), ' ') enddef ! def ExtendRef(Ref: func(string), add: string) Ref(add) enddef --- 1204,1210 ---- return join(Ref(), ' ') enddef ! def ExtendRef(Ref: func(string): list, add: string) Ref(add) enddef *************** *** 1408,1414 **** enddef def Test_wrong_dict_key_type() ! assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1029:') enddef def Line_continuation_in_def(dir: string = ''): string --- 1408,1414 ---- enddef def Test_wrong_dict_key_type() ! assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1012:') enddef def Line_continuation_in_def(dir: string = ''): string *************** *** 1422,1428 **** Line_continuation_in_def('.')->assert_equal('full') enddef ! def Line_continuation_in_lambda(): list let x = range(97, 100) ->map({_, v -> nr2char(v) ->toupper()}) --- 1422,1428 ---- Line_continuation_in_def('.')->assert_equal('full') enddef ! def Line_continuation_in_lambda(): list let x = range(97, 100) ->map({_, v -> nr2char(v) ->toupper()}) *** ../vim-8.2.1690/src/testdir/test_vim9_script.vim 2020-09-14 22:23:50.641827118 +0200 --- src/testdir/test_vim9_script.vim 2020-09-16 13:06:36.507113488 +0200 *************** *** 180,188 **** CheckDefFailure(['¬ex += 3'], 'E113:') CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') CheckDefFailure(['&ts = [7]'], 'E1012:') ! CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list') CheckDefFailure(['&ts = "xx"'], 'E1012:') ! CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string') CheckDefFailure(['&path += 3'], 'E1012:') CheckDefExecFailure(['&bs = "asdf"'], 'E474:') # test freeing ISN_STOREOPT --- 180,188 ---- CheckDefFailure(['¬ex += 3'], 'E113:') CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') CheckDefFailure(['&ts = [7]'], 'E1012:') ! CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list') CheckDefFailure(['&ts = "xx"'], 'E1012:') ! CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string') CheckDefFailure(['&path += 3'], 'E1012:') CheckDefExecFailure(['&bs = "asdf"'], 'E474:') # test freeing ISN_STOREOPT *************** *** 958,971 **** try # string slice returns a string, not a number n = g:astring[3] ! catch /E1029:/ n = 77 endtry assert_equal(77, n) try n = l[g:astring] ! catch /E1029:/ n = 88 endtry assert_equal(88, n) --- 958,971 ---- try # string slice returns a string, not a number n = g:astring[3] ! catch /E1012:/ n = 77 endtry assert_equal(77, n) try n = l[g:astring] ! catch /E1012:/ n = 88 endtry assert_equal(88, n) *************** *** 1016,1022 **** let nd: dict try nd = {g:anumber: 1} ! catch /E1029:/ n = 266 endtry assert_equal(266, n) --- 1016,1022 ---- let nd: dict try nd = {g:anumber: 1} ! catch /E1012:/ n = 266 endtry assert_equal(266, n) *************** *** 1030,1036 **** try &ts = g:astring ! catch /E1029:/ n = 288 endtry assert_equal(288, n) --- 1030,1036 ---- try &ts = g:astring ! catch /E1012:/ n = 288 endtry assert_equal(288, n) *************** *** 3184,3189 **** --- 3184,3207 ---- CheckScriptSuccess(lines) enddef + let g:dict_number = #{one: 1, two: 2} + + def Test_let_list_dict_type() + let ll: list + ll = [1, 2, 2, 3, 3, 3]->uniq() + ll->assert_equal([1, 2, 3]) + + let dd: dict + dd = g:dict_number + dd->assert_equal(g:dict_number) + + let lines =<< trim END + let ll: list + ll = [1, 2, 3]->map('"one"') + END + CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list but got list') + enddef + def Test_forward_declaration() let lines =<< trim END vim9script *** ../vim-8.2.1690/src/version.c 2020-09-15 21:34:14.998383526 +0200 --- src/version.c 2020-09-16 15:07:25.270406129 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1691, /**/ -- ERIC IDLE PLAYED: THE DEAD COLLECTOR, MR BINT (A VILLAGE NE'ER-DO -WELL VERY KEEN ON BURNING WITCHES), SIR ROBIN, THE GUARD WHO DOESN'T HICOUGH BUT TRIES TO GET THINGS STRAIGHT, CONCORDE (SIR LAUNCELOT'S TRUSTY STEED), ROGER THE SHRUBBER (A SHRUBBER), BROTHER MAYNARD "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///