From 7bc150f6d87b836b7690f8d73ee815ea8c718b13 Mon Sep 17 00:00:00 2001
From: Kaz Kylheku <kaz@kylheku.com>
Date: Tue, 3 Apr 2018 20:36:23 -0700
Subject: packages: fix package prefix read/print issue.

Suppose that we have two symbols of the same name, in two
packages: foo:sym and bar:sym.  Suppose that the bar package
has foo in its package fallback list, and suppose bar is the
current package. Then bar:sym prints without a package prefix,
as just sym. However, this is potentially ambiguous.  Suppose
that bar:sym is written to a file as just sym.  Then later the
file is read into a fresh image in a situation in which
bar:sym has not yet been interned, but foo:sym already exists.
In this situation, sym will just resolve to foo:sym.

The printer must detect this ambiguous situation. If a symbol
is present in a package, but a same-named symbol is in the
fallback list; or if a symbol is visible in the fallback list,
but a same-named symbol is present in the package, then
a package prefix should be printed.

* lib.c (symbol_needs_prefix): New function.
(unquote_star_check, obj_print_impl): Use symbol_needs_prefix
rather than symbol_visible.

* lib.h (symbol_needs_prefix): Declared.
---
 lib.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

(limited to 'lib.c')

diff --git a/lib.c b/lib.c
index b3169d2c..f089ee90 100644
--- a/lib.c
+++ b/lib.c
@@ -5430,6 +5430,48 @@ val symbol_visible(val package, val sym)
   return nil;
 }
 
+/* symbol_needs_prefix assumes the perspective that package
+ * is the current package!
+ */
+val symbol_needs_prefix(val package, val sym)
+{
+  val name = symbol_name(sym);
+  type_check (package, PKG);
+
+  {
+    int homed_here = (sym->s.package == package);
+    val home_cell = gethash_e(package->pk.symhash, name);
+    int present_here = (eq(cdr(home_cell), sym) != nil);
+    val fallback = get_hash_userdata(package->pk.symhash);
+    val fb_cell = nil;
+    int in_fallback = 0;
+
+    for (; fallback; fallback = cdr(fallback)) {
+      val fb_pkg = car(fallback);
+      val cell = gethash_e(fb_pkg->pk.symhash, name);
+
+      if (cell) {
+        fb_cell = cell;
+        if (eq(cdr(cell), sym) != nil) {
+          in_fallback = 1;
+          break;
+        }
+      }
+    }
+
+    if (!homed_here && !present_here && !in_fallback)
+      return t;
+
+    if ((homed_here || present_here) && fb_cell)
+      return t;
+
+    if (in_fallback && home_cell)
+      return t;
+  }
+
+  return nil;
+}
+
 val find_symbol(val str, val package_in)
 {
   val self = lit("find-symbol");
@@ -10953,7 +10995,7 @@ static int unquote_star_check(val obj, val pretty)
     return 0;
   if (car(obj->s.name) != chr('*'))
     return 0;
-  return pretty || symbol_visible(cur_package, obj);
+  return pretty || !symbol_needs_prefix(cur_package, obj);
 }
 
 val obj_print_impl(val obj, val out, val pretty, struct strm_ctx *ctx)
@@ -11215,7 +11257,7 @@ dot:
         put_string(lit("#:"), out);
       } else if (obj->s.package == keyword_package) {
         put_char(chr(':'), out);
-      } else if (!symbol_visible(cur_package, obj)) {
+      } else if (symbol_needs_prefix(cur_package, obj)) {
         put_string(obj->s.package->pk.name, out);
         put_char(chr(':'), out);
       }
-- 
cgit v1.2.3