patch 9.1.0548: it's not possible to get a unique id for some vars

Problem:  it's not possible to get a unique id for some vars
Solution: Add the id() Vim script function, which returns a unique
          identifier for object, dict, list, job, blob or channel
          variables (Ernie Rael)

fixes: #14374
closes: #15145

Signed-off-by: Ernie Rael <errael@raelity.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 5e3122d..370b26b 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -82,6 +82,7 @@
 static void f_hlID(typval_T *argvars, typval_T *rettv);
 static void f_hlexists(typval_T *argvars, typval_T *rettv);
 static void f_hostname(typval_T *argvars, typval_T *rettv);
+static void f_id(typval_T *argvars, typval_T *rettv);
 static void f_index(typval_T *argvars, typval_T *rettv);
 static void f_indexof(typval_T *argvars, typval_T *rettv);
 static void f_input(typval_T *argvars, typval_T *rettv);
@@ -2207,6 +2208,8 @@
 			ret_string,	    f_hostname},
     {"iconv",		3, 3, FEARG_1,	    arg3_string,
 			ret_string,	    f_iconv},
+    {"id",		1, 1, FEARG_1,	    NULL,
+			ret_string,	    f_id},
     {"indent",		1, 1, FEARG_1,	    arg1_lnum,
 			ret_number,	    f_indent},
     {"index",		2, 4, FEARG_1,	    arg24_index,
@@ -7517,6 +7520,40 @@
 }
 
 /*
+ * "id()" function
+ * Identity. Return address of item as a hex string, %p format.
+ * Currently only valid for object/container types.
+ * Return empty string if not an object.
+ */
+    void
+f_id(typval_T *argvars, typval_T *rettv)
+{
+    char_u	numbuf[NUMBUFLEN];
+
+    switch (argvars[0].v_type)
+    {
+	case VAR_LIST:
+	case VAR_DICT:
+	case VAR_OBJECT:
+	case VAR_JOB:
+	case VAR_CHANNEL:
+	case VAR_BLOB:
+	    // Assume pointer value in typval_T vval union at common location.
+	    if (argvars[0].vval.v_object != NULL)
+		vim_snprintf((char*)numbuf, sizeof(numbuf), "%p",
+					    (void *)argvars[0].vval.v_object);
+	    else
+		numbuf[0] = NUL;
+	    break;
+	default:
+	    numbuf[0] = NUL;
+    }
+
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = vim_strsave(numbuf);
+}
+
+/*
  * "index()" function
  */
     static void
diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim
index 0a4f88c..48217cf 100644
--- a/src/testdir/test_listdict.vim
+++ b/src/testdir/test_listdict.vim
@@ -1619,4 +1619,34 @@
   call v9.CheckLegacyAndVim9Success(lines)
 endfunc
 
+" Test for using id()
+def Test_id_with_dict()
+  # demonstate a way that "id(item)" differs from "string(item)"
+  var d1 = {one: 1}
+  var d2 = {one: 1}
+  var d3 = {one: 1}
+  var idDict: dict<any>
+  idDict[id(d1)] = d1
+  idDict[id(d2)] = d2
+  idDict[id(d3)] = d3
+  assert_equal(3, idDict->len())
+
+  var stringDict: dict<any>
+  stringDict[string(d1)] = d1
+  stringDict[string(d2)] = d2
+  stringDict[string(d3)] = d3
+  assert_equal(1, stringDict->len())
+
+  assert_equal('', id(3))
+
+  assert_equal('', id(null))
+  assert_equal('', id(null_blob))
+  assert_equal('', id(null_dict))
+  assert_equal('', id(null_function))
+  assert_equal('', id(null_list))
+  assert_equal('', id(null_partial))
+  assert_equal('', id(null_string))
+  assert_equal('', id(null_channel))
+  assert_equal('', id(null_job))
+enddef
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index ce6b8d6..1f334ca 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    548,
+/**/
     547,
 /**/
     546,