changeset 33682:f126ffc85f7c v9.0.2078

patch 9.0.2078: several problems with type aliases Commit: https://github.com/vim/vim/commit/feaccd239573a6265d39d3a917862ee40742eab4 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Oct 28 15:53:55 2023 +0200 patch 9.0.2078: several problems with type aliases Problem: several problems with type aliases Solution: Check for more error conditions, add tests, fix issues Check for more error conditions and add additional tests fixes #13434 fixes #13437 fixes #13438 closes #13441 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sat, 28 Oct 2023 16:00:06 +0200
parents 9d9f6d05091d
children dfdf61d163a3
files runtime/doc/vim9class.txt src/errors.h src/eval.c src/evalvars.c src/proto/typval.pro src/testdir/test_vim9_script.vim src/typval.c src/version.c src/vim9class.c src/vim9execute.c src/vim9expr.c src/vim9type.c
diffstat 12 files changed, 264 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -749,7 +749,9 @@ type alias.  For Example: >
 	:type ListOfStrings = list<string>
 
 The type alias can be used wherever a built-in type can be used.  The type
-alias name must start with an upper case character.
+alias name must start with an upper case character.  A type alias can be
+created only at the script level and not inside a function.  A type alias can
+be exported and used across scripts.
 
 ==============================================================================
 
--- a/src/errors.h
+++ b/src/errors.h
@@ -3542,8 +3542,8 @@ EXTERN char e_type_can_only_be_defined_i
 	INIT(= N_("E1393: Type can only be defined in Vim9 script"));
 EXTERN char e_type_name_must_start_with_uppercase_letter_str[]
 	INIT(= N_("E1394: Type name must start with an uppercase letter: %s"));
-EXTERN char e_using_typealias_as_variable[]
-	INIT(= N_("E1395: Type alias \"%s\" cannot be used as a variable"));
+EXTERN char e_cannot_modify_typealias[]
+	INIT(= N_("E1395: Type alias \"%s\" cannot be modified"));
 EXTERN char e_typealias_already_exists_for_str[]
 	INIT(= N_("E1396: Type alias \"%s\" already exists"));
 EXTERN char e_missing_typealias_name[]
@@ -3552,8 +3552,16 @@ EXTERN char e_missing_typealias_type[]
 	INIT(= N_("E1398: Missing type alias type"));
 EXTERN char e_type_can_only_be_used_in_script[]
 	INIT(= N_("E1399: Type can only be used in a script"));
-#endif
-// E1400 - E1499 unused (reserved for Vim9 class support)
+EXTERN char e_using_typealias_as_number[]
+	INIT(= N_("E1400: Using type alias \"%s\" as a Number"));
+EXTERN char e_using_typealias_as_float[]
+	INIT(= N_("E1401: Using type alias \"%s\" as a Float"));
+EXTERN char e_using_typealias_as_string[]
+	INIT(= N_("E1402: Using type alias \"%s\" as a String"));
+EXTERN char e_using_typealias_as_value[]
+	INIT(= N_("E1403: Type alias \"%s\" cannot be used as a value"));
+#endif
+// E1404 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
 	INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
--- a/src/eval.c
+++ b/src/eval.c
@@ -1885,7 +1885,7 @@ set_var_lval(
 	    {
 		if (di != NULL && di->di_tv.v_type == VAR_TYPEALIAS)
 		{
-		    semsg(_(e_using_typealias_as_variable),
+		    semsg(_(e_cannot_modify_typealias),
 					di->di_tv.vval.v_typealias->ta_name);
 		    clear_tv(&tv);
 		    return;
@@ -4045,7 +4045,8 @@ eval8(
 
 	    if (!equal_type(want_type, actual, 0))
 	    {
-		if (want_type == &t_bool && actual != &t_bool
+		if (want_type->tt_type == VAR_BOOL
+					&& actual->tt_type != VAR_BOOL
 					&& (actual->tt_flags & TTFLAG_BOOL_OK))
 		{
 		    int n = tv2bool(rettv);
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -1838,7 +1838,7 @@ ex_let_one(
 
     if (tv->v_type == VAR_TYPEALIAS)
     {
-	semsg(_(e_using_typealias_as_variable), tv->vval.v_typealias->ta_name);
+	semsg(_(e_using_typealias_as_value), tv->vval.v_typealias->ta_name);
 	return NULL;
     }
 
@@ -3979,7 +3979,7 @@ set_var_const(
 
 	    if (di->di_tv.v_type == VAR_TYPEALIAS)
 	    {
-		semsg(_(e_using_typealias_as_variable),
+		semsg(_(e_cannot_modify_typealias),
 					    di->di_tv.vval.v_typealias->ta_name);
 		clear_tv(&di->di_tv);
 		goto failed;
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -52,6 +52,7 @@ int check_for_list_or_dict_or_blob_arg(t
 int check_for_list_or_dict_or_blob_or_string_arg(typval_T *args, int idx);
 int check_for_opt_buffer_or_dict_arg(typval_T *args, int idx);
 int check_for_object_arg(typval_T *args, int idx);
+int tv_class_alias(typval_T *tv);
 int check_for_class_or_list_arg(typval_T *args, int idx);
 char_u *tv_get_string(typval_T *varp);
 char_u *tv_get_string_strict(typval_T *varp);
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -4785,16 +4785,15 @@ enddef
 
 " Test for :type command to create type aliases
 def Test_typealias()
+  # Use type alias at script level
   var lines =<< trim END
     vim9script
     type ListOfStrings = list<string>
-    var a: ListOfStrings = ['a', 'b']
-    assert_equal(['a', 'b'], a)
-    def Foo(b: ListOfStrings): ListOfStrings
-      var c: ListOfStrings = ['c', 'd']
-      assert_equal(['c', 'd'], c)
-      return b
+    def Foo(a: ListOfStrings): ListOfStrings
+      return a
     enddef
+    var b: ListOfStrings = ['a', 'b']
+    assert_equal(['a', 'b'], b)
     assert_equal(['e', 'f'], Foo(['e', 'f']))
     assert_equal('typealias<list<string>>', typename(ListOfStrings))
     assert_equal(v:t_typealias, type(ListOfStrings))
@@ -4803,6 +4802,26 @@ def Test_typealias()
   END
   v9.CheckSourceSuccess(lines)
 
+  # Use type alias at def function level
+  lines =<< trim END
+    vim9script
+    type ListOfStrings = list<string>
+    def Foo(a: ListOfStrings): ListOfStrings
+      return a
+    enddef
+    def Bar()
+      var c: ListOfStrings = ['c', 'd']
+      assert_equal(['c', 'd'], c)
+      assert_equal(['e', 'f'], Foo(['e', 'f']))
+      assert_equal('typealias<list<string>>', typename(ListOfStrings))
+      assert_equal(v:t_typealias, type(ListOfStrings))
+      assert_equal('ListOfStrings', string(ListOfStrings))
+      assert_equal(false, null == ListOfStrings)
+    enddef
+    Bar()
+  END
+  v9.CheckSourceSuccess(lines)
+
   # Use :type outside a Vim9 script
   lines =<< trim END
     type Index = number
@@ -4861,9 +4880,9 @@ def Test_typealias()
   # type alias starting with lower-case letter
   lines =<< trim END
     vim9script
-    type index number
-  END
-  v9.CheckSourceFailure(lines, 'E1394: Type name must start with an uppercase letter: index number', 2)
+    type index = number
+  END
+  v9.CheckSourceFailure(lines, 'E1394: Type name must start with an uppercase letter: index = number', 2)
 
   # No white space following the alias name
   lines =<< trim END
@@ -4895,27 +4914,74 @@ def Test_typealias()
   END
   v9.CheckSourceFailure(lines, 'E1396: Type alias "MyList" already exists', 3)
 
-  # Sourcing a script twice (which will free script local variables)
+  # def function argument name collision with a type alias
+  lines =<< trim END
+    vim9script
+    type A = list<number>
+    def Foo(A: number)
+    enddef
+  END
+  v9.CheckSourceFailure(lines, 'E1168: Argument already declared in the script: A: number)', 3)
+
+  # def function local variable name collision with a type alias
+  lines =<< trim END
+    vim9script
+    type A = list<number>
+    def Foo()
+      var A: number = 10
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1054: Variable already declared in the script: A', 1)
+
+  # type alias a variable
+  lines =<< trim END
+    vim9script
+    var A: list<number> = []
+    type B = A
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: A', 3)
+
+  # type alias a class
   lines =<< trim END
     vim9script
     class C
     endclass
     type AC = C
-    assert_equal('typealias<object<C>>', typename(AC))
-  END
+    assert_equal('class<C>', typename(AC))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Sourcing a script twice (which will free script local variables)
+  # Uses "lines" from the previous test
   new
   setline(1, lines)
   :source
   :source
   bw!
 
+  # type alias a type alias
+  lines =<< trim END
+    vim9script
+    type A = string
+    type B = A
+    var b: B = 'abc'
+    assert_equal('abc', b)
+    def Foo()
+      var c: B = 'def'
+      assert_equal('def', c)
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+
   # Assigning to a type alias (script level)
   lines =<< trim END
     vim9script
     type MyType = list<number>
     MyType = [1, 2, 3]
   END
-  v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 3)
+  v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be modified', 3)
 
   # Assigning a type alias (def function level)
   lines =<< trim END
@@ -4926,16 +4992,18 @@ def Test_typealias()
     enddef
     Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1)
+  v9.CheckSourceFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 1)
 
   # Using type alias in an expression (script level)
   lines =<< trim END
     vim9script
     type MyType = list<number>
-    assert_fails('var m = MyType', 'E1395: Type alias "MyType" cannot be used as a variable')
-    assert_fails('var i = MyType + 1', 'E1395: Type alias "MyType" cannot be used as a variable')
-    assert_fails('var f = 1.0 + MyType', 'E1395: Type alias "MyType" cannot be used as a variable')
-    assert_fails('MyType += 10', 'E1395: Type alias "MyType" cannot be used as a variable')
+    assert_fails('var m = MyType', 'E1403: Type alias "MyType" cannot be used as a value')
+    assert_fails('var i = MyType + 1', 'E1400: Using type alias "MyType" as a Number')
+    assert_fails('var f = 1.0 + MyType', 'E1400: Using type alias "MyType" as a Number')
+    assert_fails('MyType += 10', 'E1395: Type alias "MyType" cannot be modified')
+    assert_fails('var x = $"-{MyType}-"', 'E1402: Using type alias "MyType" as a String')
+    assert_fails('var x = MyType[1]', 'E909: Cannot index a special variable')
   END
   v9.CheckSourceSuccess(lines)
 
@@ -4948,7 +5016,7 @@ def Test_typealias()
     enddef
     Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 1)
+  v9.CheckSourceFailure(lines, 'E1051: Wrong argument type for +', 1)
 
   # Using type alias in an expression (def function level)
   lines =<< trim END
@@ -4972,6 +5040,25 @@ def Test_typealias()
   END
   v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "MyType"', 1)
 
+  # Convert type alias to a string (def function level)
+  lines =<< trim END
+    vim9script
+    type MyType = list<number>
+    def Foo()
+      var x = $"-{MyType}-"
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1105: Cannot convert typealias to string', 1)
+
+  # Using type alias as a float
+  lines =<< trim END
+    vim9script
+    type B = number
+    sort([1.1, B], 'f')
+  END
+  v9.CheckSourceFailure(lines, 'E1401: Using type alias "B" as a Float', 3)
+
   # Creating a typealias in a def function
   lines =<< trim END
     vim9script
@@ -5010,11 +5097,19 @@ def Test_typealias()
     enddef
     Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 2)
+  v9.CheckSourceFailure(lines, 'E1072: Cannot compare typealias with number', 2)
+
+  # casting a number to a type alias (script level)
+  lines =<< trim END
+    vim9script
+    type MyType = bool
+    assert_equal(true, <MyType>1 == true)
+  END
+  v9.CheckSourceSuccess(lines)
 enddef
 
 " Test for exporting and importing type aliases
-def Test_import_typealias()
+def Test_typealias_import()
   var lines =<< trim END
     vim9script
     export type MyType = list<number>
@@ -5073,6 +5168,24 @@ def Test_import_typealias()
     assert_equal([4, 5, 6], myList2)
   END
   v9.CheckScriptSuccess(lines)
+
+  # Using an exported class to create a type alias
+  lines =<< trim END
+    vim9script
+    export class MyClass
+      this.val = 10
+    endclass
+  END
+  writefile(lines, 'Xtypeexport4.vim', 'D')
+  lines =<< trim END
+    vim9script
+    import './Xtypeexport4.vim' as T
+
+    type MyType3 = T.MyClass
+    var c: MyType3 = T.MyClass.new()
+    assert_equal(10, c.val)
+  END
+  v9.CheckScriptSuccess(lines)
 enddef
 
 " Test for using typealias as a def function argument and return type
@@ -5131,6 +5244,17 @@ def Test_typealias_with_builtin_function
   END
   v9.CheckScriptFailure(lines, 'E701: Invalid type for len()', 3)
 
+  # Using a type alias with len()
+  lines =<< trim END
+    vim9script
+    type A = list<func>
+    def Foo()
+      var x = len(A)
+    enddef
+    Foo()
+  END
+  v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected list<any> but got typealias', 1)
+
   # Using a type alias with eval()
   lines =<< trim END
     vim9script
@@ -5140,7 +5264,7 @@ def Test_typealias_with_builtin_function
     enddef
     Foo()
   END
-  v9.CheckScriptFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1)
+  v9.CheckScriptFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 1)
 enddef
 
 " Test for type alias refcount
@@ -5161,6 +5285,37 @@ def Test_typealias_refcount()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using instanceof() with a type alias
+def Test_typealias_instanceof()
+  var lines =<< trim END
+    vim9script
+    class C
+    endclass
+
+    type Ctype = C
+    var o = C.new()
+    assert_equal(1, instanceof(o, Ctype))
+    type Ntype = number
+    assert_fails('instanceof(o, Ntype)', 'E693: List or Class required for argument 2')
+    assert_equal(1, instanceof(o, [Ctype]))
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for type aliasing a class
+def Test_typealias_class()
+  var lines =<< trim END
+    vim9script
+    class C
+      this.color = 'green'
+    endclass
+    type MyClass = C
+    var o: MyClass = MyClass.new()
+    assert_equal('green', o.color)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " Keep this last, it messes up highlighting.
 def Test_substitute_cmd()
   new
--- a/src/typval.c
+++ b/src/typval.c
@@ -271,7 +271,7 @@ tv_get_bool_or_number_chk(
 	    emsg(_(e_cannot_use_void_value));
 	    break;
 	case VAR_TYPEALIAS:
-	    semsg(_(e_using_typealias_as_variable),
+	    semsg(_(e_using_typealias_as_number),
 					varp->vval.v_typealias->ta_name);
 	    break;
 	case VAR_UNKNOWN:
@@ -392,7 +392,7 @@ tv_get_float_chk(typval_T *varp, int *er
 	    emsg(_(e_cannot_use_void_value));
 	    break;
 	case VAR_TYPEALIAS:
-	    semsg(_(e_using_typealias_as_variable),
+	    semsg(_(e_using_typealias_as_float),
 					varp->vval.v_typealias->ta_name);
 	    break;
 	case VAR_UNKNOWN:
@@ -1004,12 +1004,23 @@ check_for_object_arg(typval_T *args, int
 }
 
 /*
+ * Returns TRUE if "tv" is a type alias for a class
+ */
+    int
+tv_class_alias(typval_T *tv)
+{
+    return tv->v_type == VAR_TYPEALIAS &&
+			tv->vval.v_typealias->ta_type->tt_type == VAR_OBJECT;
+}
+
+/*
  * Give an error and return FAIL unless "args[idx]" is a class or a list.
  */
     int
 check_for_class_or_list_arg(typval_T *args, int idx)
 {
-    if (args[idx].v_type != VAR_CLASS && args[idx].v_type != VAR_LIST)
+    if (args[idx].v_type != VAR_CLASS && args[idx].v_type != VAR_LIST
+					&& !tv_class_alias(&args[idx]))
     {
 	semsg(_(e_list_or_class_required_for_argument_nr), idx + 1);
 	return FAIL;
@@ -1146,6 +1157,9 @@ tv_get_string_buf_chk_strict(typval_T *v
 	    emsg(_(e_cannot_use_void_value));
 	    break;
 	case VAR_TYPEALIAS:
+	    semsg(_(e_using_typealias_as_string),
+					varp->vval.v_typealias->ta_name);
+	    break;
 	case VAR_UNKNOWN:
 	case VAR_ANY:
 	case VAR_INSTR:
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2078,
+/**/
     2077,
 /**/
     2076,
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2196,13 +2196,26 @@ ex_type(exarg_T *eap UNUSED)
 	goto done;
     }
 
-    // Add the user-defined type to the script-local variables.
-    tv.v_type = VAR_TYPEALIAS;
-    tv.v_lock = 0;
-    tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
-    ++tv.vval.v_typealias->ta_refcount;
-    tv.vval.v_typealias->ta_name = vim_strsave(name_start);
-    tv.vval.v_typealias->ta_type = type;
+    // Create a script-local variable for the type alias.
+    if (type->tt_type != VAR_OBJECT)
+    {
+	tv.v_type = VAR_TYPEALIAS;
+	tv.v_lock = 0;
+	tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
+	++tv.vval.v_typealias->ta_refcount;
+	tv.vval.v_typealias->ta_name = vim_strsave(name_start);
+	tv.vval.v_typealias->ta_type = type;
+    }
+    else
+    {
+	// When creating a type alias for a class, use the class type itself to
+	// create the type alias variable.  This is needed to use the type
+	// alias to invoke class methods (e.g. new()) and use class variables.
+	tv.v_type = VAR_CLASS;
+	tv.v_lock = 0;
+	tv.vval.v_class = type->tt_class;
+	++tv.vval.v_class->class_refcount;
+    }
     set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
 						ASSIGN_CONST | ASSIGN_FINAL, 0);
 
@@ -3155,6 +3168,7 @@ f_instanceof(typval_T *argvars, typval_T
     typval_T	*object_tv = &argvars[0];
     typval_T	*classinfo_tv = &argvars[1];
     listitem_T	*li;
+    class_T	*c;
 
     rettv->vval.v_number = VVAL_FALSE;
 
@@ -3169,25 +3183,35 @@ f_instanceof(typval_T *argvars, typval_T
     {
 	FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
 	{
-	    if (li->li_tv.v_type != VAR_CLASS)
+	    if (li->li_tv.v_type != VAR_CLASS && !tv_class_alias(&li->li_tv))
 	    {
 		emsg(_(e_class_required));
 		return;
 	    }
 
-	    if (class_instance_of(object_tv->vval.v_object->obj_class,
-			li->li_tv.vval.v_class) == TRUE)
+	    if (li->li_tv.v_type == VAR_TYPEALIAS)
+		c = li->li_tv.vval.v_typealias->ta_type->tt_class;
+	    else
+		c = li->li_tv.vval.v_class;
+
+	    if (class_instance_of(object_tv->vval.v_object->obj_class, c)
+								== TRUE)
 	    {
 		rettv->vval.v_number = VVAL_TRUE;
 		return;
 	    }
 	}
+
+	return;
     }
-    else if (classinfo_tv->v_type == VAR_CLASS)
-    {
-	rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class,
-		classinfo_tv->vval.v_class);
-    }
+
+    if (classinfo_tv->v_type == VAR_TYPEALIAS)
+	c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
+    else
+	c = classinfo_tv->vval.v_class;
+
+    rettv->vval.v_number =
+		class_instance_of(object_tv->vval.v_object->obj_class, c);
 }
 
 #endif // FEAT_EVAL
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3809,7 +3809,7 @@ exec_instructions(ectx_T *ectx)
 		tv = STACK_TV_VAR(iptr->isn_arg.number);
 		if (STACK_TV_BOT(0)->v_type == VAR_TYPEALIAS)
 		{
-		    semsg(_(e_using_typealias_as_variable),
+		    semsg(_(e_using_typealias_as_value),
 				STACK_TV_BOT(0)->vval.v_typealias->ta_name);
 		    clear_tv(STACK_TV_BOT(0));
 		    goto on_error;
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -530,13 +530,6 @@ compile_load_scriptvar(
     {
 	svar_T		*sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
 
-	if (sv->sv_tv->v_type == VAR_TYPEALIAS)
-	{
-	    semsg(_(e_using_typealias_as_variable),
-				sv->sv_tv->vval.v_typealias->ta_name);
-	    return FAIL;
-	}
-
 	generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT,
 					current_sctx.sc_sid, idx, sv->sv_type);
 	return OK;
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -1811,7 +1811,13 @@ f_typename(typval_T *argvars, typval_T *
     rettv->v_type = VAR_STRING;
     ga_init2(&type_list, sizeof(type_T *), 10);
     if (argvars[0].v_type == VAR_TYPEALIAS)
-	type = argvars[0].vval.v_typealias->ta_type;
+    {
+	type = copy_type(argvars[0].vval.v_typealias->ta_type, &type_list);
+	// A type alias for a class has the type set to VAR_OBJECT.  Change it
+	// to VAR_CLASS, so that the name is "typealias<class<xxx>>"
+	if (type->tt_type == VAR_OBJECT)
+	    type->tt_type = VAR_CLASS;
+    }
     else
 	type = typval2type(argvars, get_copyID(), &type_list, TVTT_DO_MEMBER);
     name = type_name(type, &tofree);