Merge pull request #19416 from jckarter/better-missing-metadata-warnings-4.2

[4.2] Make runtime warnings about missing metadata more descriptive.
diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h
index 792eb85..a575286 100644
--- a/include/swift/Runtime/Debug.h
+++ b/include/swift/Runtime/Debug.h
@@ -97,6 +97,10 @@
 
 /// swift::warning() emits a warning from the runtime.
 extern void
+warningv(uint32_t flags, const char *format, va_list args);
+
+/// swift::warning() emits a warning from the runtime.
+extern void
 warning(uint32_t flags, const char *format, ...);
 
 // swift_dynamicCastFailure halts using fatalError()
diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp
index c3f1781..ff49065 100644
--- a/stdlib/public/runtime/Errors.cpp
+++ b/stdlib/public/runtime/Errors.cpp
@@ -349,22 +349,29 @@
 
 // Report a warning to system console and stderr.
 void
-swift::warning(uint32_t flags, const char *format, ...)
+swift::warningv(uint32_t flags, const char *format, va_list args)
 {
-  va_list args;
-  va_start(args, format);
-
   char *log;
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wuninitialized"
   swift_vasprintf(&log, format, args);
 #pragma GCC diagnostic pop
-
+  
   reportNow(flags, log);
-
+  
   free(log);
 }
 
+// Report a warning to system console and stderr.
+void
+swift::warning(uint32_t flags, const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+
+  warningv(flags, format, args);
+}
+
 // Crash when a deleted method is called by accident.
 SWIFT_RUNTIME_EXPORT
 LLVM_ATTRIBUTE_NORETURN
diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp
index be28b62..d8344b7 100644
--- a/stdlib/public/runtime/MetadataLookup.cpp
+++ b/stdlib/public/runtime/MetadataLookup.cpp
@@ -1115,6 +1115,47 @@
     });
 }
 
+struct swift_closure {
+  void *fptr;
+  HeapObject *context;
+};
+SWIFT_RUNTIME_STDLIB_API SWIFT_CC(swift) swift_closure
+MANGLE_SYM(s20_playgroundPrintHookySScSgvg)();
+
+static bool _shouldReportMissingReflectionMetadataWarnings() {
+  // Missing metadata warnings noise up playground sessions and aren't really
+  // actionable in playground contexts. If we're running in a playground,
+  // suppress warnings.
+  //
+  // Guesstimate whether we're in a playground by looking at the
+  // _playgroundPrintHook variable in the standard library, which is set during
+  // playground execution.
+  auto hook = MANGLE_SYM(s20_playgroundPrintHookySScSgvg)();
+  if (hook.fptr) {
+    swift_release(hook.context);
+    return false;
+  } else {
+    return true;
+  }
+}
+
+/// Raise a warning about reflection metadata that could not be found
+/// at runtime. This is usually mostly harmless, but it's good to alert
+/// users that it happens.
+static void
+missing_reflection_metadata_warning(const char *fmt, ...) {
+  bool shouldWarn =
+    SWIFT_LAZY_CONSTANT(_shouldReportMissingReflectionMetadataWarnings());
+  
+  if (!shouldWarn)
+    return;
+  
+  va_list args;
+  va_start(args, fmt);
+  
+  warningv(0, fmt, args);
+}
+
 void swift::swift_getFieldAt(
   const Metadata *base, unsigned index, 
   void (*callback)(const char *name, const Metadata *type, void *ctx), void *callbackCtx) {
@@ -1182,10 +1223,12 @@
     // a log message.
     if (typeInfo == nullptr) {
       typeInfo = TypeInfo(&METADATA_SYM(EMPTY_TUPLE_MANGLING), {});
-      warning(0, "SWIFT RUNTIME BUG: unable to demangle type of field '%*s'. "
-                 "mangled type name is '%*s'\n",
-                 (int)name.size(), name.data(),
-                 (int)typeName.size(), typeName.data());
+      missing_reflection_metadata_warning(
+        "warning: the Swift runtime was unable to demangle the type "
+        "of field '%*s'. the mangled type name is '%*s'. this field will "
+        "show up as an empty tuple in Mirrors\n",
+        (int)name.size(), name.data(),
+        (int)typeName.size(), typeName.data());
     }
 
     callback(name, FieldType()
@@ -1235,8 +1278,11 @@
   // If we failed to find the field descriptor metadata for the type, fall
   // back to returning an empty tuple as a standin.
   auto typeName = swift_getTypeName(base, /*qualified*/ true);
-  warning(0, "SWIFT RUNTIME BUG: unable to find field metadata for type '%*s'\n",
-             (int)typeName.length, typeName.data);
+  missing_reflection_metadata_warning(
+    "warning: the Swift runtime found no field metadata for "
+    "type '%*s' that claims to be reflectable. Its fields will show up as "
+    "'unknown' in Mirrors\n",
+    (int)typeName.length, typeName.data);
   callback("unknown",
            FieldType()
              .withType(TypeInfo(&METADATA_SYM(EMPTY_TUPLE_MANGLING), {}))
diff --git a/unittests/runtime/Stdlib.cpp b/unittests/runtime/Stdlib.cpp
index 04ecf72..def4bd6 100644
--- a/unittests/runtime/Stdlib.cpp
+++ b/unittests/runtime/Stdlib.cpp
@@ -252,3 +252,15 @@
 // type metadata accessor for Swift._ClassMirror
 SWIFT_RUNTIME_STDLIB_INTERNAL
 const long long $Ss12_ClassMirrorVMa[1] = {0};
+
+// playground print hook
+
+struct swift_closure {
+  void *fptr;
+  HeapObject *context;
+};
+SWIFT_RUNTIME_STDLIB_API SWIFT_CC(swift) swift_closure
+MANGLE_SYM(s20_playgroundPrintHookySScSgvg)() {
+  return {nullptr, nullptr};
+}
+