Extend synthetic children to produce synthetic values (as in, those that GetValueAsUnsigned(), GetValueAsCString() would return)

The way to do this is to write a synthetic child provider for your type, and have it vend the (optional) get_value function.
If get_value is defined, and it returns a valid SBValue, that SBValue's value (as in lldb_private::Value) will be used as the synthetic ValueObject's Value

The rationale for doing things this way is twofold:

- there are many possible ways to define a "value" (SBData, a Python number, ...) but SBValue seems general enough as a thing that stores a "value", so we just trade values that way and that keeps our currency trivial
- we could introduce a new level of layering (ValueObjectSyntheticValue), a new kind of formatter (synthetic value producer), but that would complicate the model (can I have a dynamic with no synthetic children but synthetic value? synthetic value with synthetic children but no dynamic?), and I really couldn't see much benefit to be reaped from this added complexity in the matrix
On the other hand, just defining a synthetic child provider with a get_value but returning no actual children is easy enough that it's not a significant road-block to adoption of this feature

Comes with a test case

llvm-svn: 219330
diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h
index 1884431..d9d6b78 100644
--- a/lldb/include/lldb/Core/ValueObject.h
+++ b/lldb/include/lldb/Core/ValueObject.h
@@ -88,6 +88,7 @@
     {
         eExpressionPathScanEndReasonEndOfString = 1,           // out of data to parse
         eExpressionPathScanEndReasonNoSuchChild,               // child element not found
+        eExpressionPathScanEndReasonNoSuchSyntheticChild,      // (synthetic) child element not found
         eExpressionPathScanEndReasonEmptyRangeNotAllowed,      // [] only allowed for arrays
         eExpressionPathScanEndReasonDotInsteadOfArrow,         // . used when -> should be used
         eExpressionPathScanEndReasonArrowInsteadOfDot,         // -> used when . should be used
@@ -379,6 +380,9 @@
     // this vends a TypeImpl that is useful at the SB API layer
     virtual TypeImpl
     GetTypeImpl ();
+    
+    virtual bool
+    CanProvideValue ();
 
     //------------------------------------------------------------------
     // Subclasses must implement the functions below.
@@ -756,6 +760,12 @@
         return false;
     }
     
+    virtual bool
+    DoesProvideSyntheticValue ()
+    {
+        return false;
+    }
+    
     virtual SymbolContextScope *
     GetSymbolContextScope();
     
diff --git a/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h
index e12698f..9846ae6 100644
--- a/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h
+++ b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h
@@ -132,11 +132,12 @@
     GetNonSyntheticValue ();
     
     virtual bool
-    ResolveValue (Scalar &scalar)
+    CanProvideValue ();
+    
+    virtual bool
+    DoesProvideSyntheticValue ()
     {
-        if (m_parent)
-            return m_parent->ResolveValue(scalar);
-        return false;
+        return (UpdateValueIfNeeded(), m_provides_value == eLazyBoolYes);
     }
     
 protected:
@@ -167,12 +168,14 @@
 
     LazyBool        m_might_have_children;
     
+    LazyBool        m_provides_value;
+    
 private:
     friend class ValueObject;
     ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter);
     
     void
-    CopyParentData ();
+    CopyValueData (ValueObject *source);
     
     //------------------------------------------------------------------
     // For ValueObject only
diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
index a25f11d..9c4f77c 100644
--- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h
+++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h
@@ -81,6 +81,11 @@
         virtual bool
         MightHaveChildren () = 0;
         
+        // if this function returns a non-null ValueObject, then the returned ValueObject will stand
+        // for this ValueObject whenever a "value" request is made to this ValueObject
+        virtual lldb::ValueObjectSP
+        GetSyntheticValue () { return nullptr; }
+        
         typedef std::shared_ptr<SyntheticChildrenFrontEnd> SharedPointer;
         typedef std::unique_ptr<SyntheticChildrenFrontEnd> AutoPointer;
         
@@ -593,6 +598,15 @@
                 return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp, name.GetCString());
             }
             
+            virtual lldb::ValueObjectSP
+            GetSyntheticValue ()
+            {
+                if (!m_wrapper_sp || m_interpreter == NULL)
+                    return nullptr;
+                
+                return m_interpreter->GetSyntheticValue(m_wrapper_sp);
+            }
+            
             typedef std::shared_ptr<SyntheticChildrenFrontEnd> SharedPointer;
             
         private:
diff --git a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
index 327ebd1..cc8b198 100644
--- a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
+++ b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
@@ -295,7 +295,7 @@
           uint32_t curr_depth);
     
     bool
-    GetDynamicValueIfNeeded ();
+    GetMostSpecializedValue ();
     
     const char*
     GetDescriptionForDisplay ();
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 2fbc57a..e7a456d 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -121,7 +121,7 @@
     typedef lldb::ValueObjectSP  (*SWIGPythonGetValueObjectSPFromSBValue)       (void* data);
     typedef bool            (*SWIGPythonUpdateSynthProviderInstance)            (void* data);
     typedef bool            (*SWIGPythonMightHaveChildrenSynthProviderInstance) (void* data);
-
+    typedef void*           (*SWIGPythonGetValueSynthProviderInstance)          (void *implementor);
     
     typedef bool            (*SWIGPythonCallCommand)            (const char *python_function_name,
                                                                  const char *session_dictionary_name,
@@ -498,6 +498,12 @@
         return true;
     }
     
+    virtual lldb::ValueObjectSP
+    GetSyntheticValue (const lldb::ScriptInterpreterObjectSP& implementor)
+    {
+        return nullptr;
+    }
+    
     virtual bool
     RunScriptBasedCommand (const char* impl_function,
                            const char* args,
@@ -607,6 +613,7 @@
                            SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue,
                            SWIGPythonUpdateSynthProviderInstance swig_update_provider,
                            SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider,
+                           SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider,
                            SWIGPythonCallCommand swig_call_command,
                            SWIGPythonCallModuleInit swig_call_module_init,
                            SWIGPythonCreateOSPlugin swig_create_os_plugin,
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h
index 92fc032..2cdb839 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h
@@ -141,6 +141,9 @@
     virtual bool
     MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor);
     
+    virtual lldb::ValueObjectSP
+    GetSyntheticValue (const lldb::ScriptInterpreterObjectSP& implementor);
+    
     virtual bool
     RunScriptBasedCommand(const char* impl_function,
                           const char* args,
@@ -285,6 +288,7 @@
                            SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue,
                            SWIGPythonUpdateSynthProviderInstance swig_update_provider,
                            SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider,
+                           SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider,
                            SWIGPythonCallCommand swig_call_command,
                            SWIGPythonCallModuleInit swig_call_module_init,
                            SWIGPythonCreateOSPlugin swig_create_os_plugin,
diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig
index bf22198..9f9ebad 100644
--- a/lldb/scripts/Python/python-wrapper.swig
+++ b/lldb/scripts/Python/python-wrapper.swig
@@ -723,6 +723,34 @@
     return ret_val;
 }
 
+SWIGEXPORT PyObject*
+LLDBSwigPython_GetValueSynthProviderInstance
+(
+    PyObject *implementor
+)
+{
+    PyObject* ret_val = nullptr;
+
+    static char callee_name[] = "get_value";
+
+    PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name, Py_None);
+
+    if (py_return == Py_None || py_return == nullptr)
+        ret_val = nullptr;
+
+    lldb::SBValue* sbvalue_ptr = NULL;
+    
+    if (SWIG_ConvertPtr(py_return, (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1)
+        ret_val = nullptr;
+    else if (sbvalue_ptr == NULL)
+        ret_val = nullptr;
+    else
+        ret_val = py_return;
+
+    Py_XDECREF(py_return);
+    return ret_val;
+}
+
 SWIGEXPORT void*
 LLDBSWIGPython_CastPyObjectToSBValue
 (
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index aa1ff55..9f6f4e8 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -474,6 +474,9 @@
 extern "C" bool
 LLDBSwigPython_MightHaveChildrenSynthProviderInstance (void* implementor);
 
+extern "C" void *
+LLDBSwigPython_GetValueSynthProviderInstance (void* implementor);
+
 extern "C" bool
 LLDBSwigPythonCallCommand (const char *python_function_name,
                            const char *session_dictionary_name,
@@ -544,6 +547,7 @@
                                                   LLDBSWIGPython_GetValueObjectSPFromSBValue,
                                                   LLDBSwigPython_UpdateSynthProviderInstance,
                                                   LLDBSwigPython_MightHaveChildrenSynthProviderInstance,
+                                                  LLDBSwigPython_GetValueSynthProviderInstance,
                                                   LLDBSwigPythonCallCommand,
                                                   LLDBSwigPythonCallModuleInit,
                                                   LLDBSWIGPythonCreateOSPlugin,
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 3b37de7..f38b7c0 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -1450,7 +1450,7 @@
                     }
                     else
                     {
-                        my_format = GetClangType().GetFormat();
+                        my_format = GetValue().GetClangType().GetFormat();
                     }
                 }
             }
@@ -1482,7 +1482,7 @@
 ValueObject::GetValueAsUnsigned (uint64_t fail_value, bool *success)
 {
     // If our byte size is zero this is an aggregate type that has children
-    if (!GetClangType().IsAggregateType())
+    if (CanProvideValue())
     {
         Scalar scalar;
         if (ResolveValue (scalar))
@@ -1503,7 +1503,7 @@
 ValueObject::GetValueAsSigned (int64_t fail_value, bool *success)
 {
     // If our byte size is zero this is an aggregate type that has children
-    if (!GetClangType().IsAggregateType())
+    if (CanProvideValue())
     {
         Scalar scalar;
         if (ResolveValue (scalar))
@@ -1751,7 +1751,7 @@
                 cstr = GetSummaryAsCString();
             else if (val_obj_display == eValueObjectRepresentationStyleSummary)
             {
-                if (GetClangType().IsAggregateType())
+                if (!CanProvideValue())
                 {
                     strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString());
                     cstr = strm.GetString().c_str();
@@ -2805,7 +2805,7 @@
                         if (root->IsSynthetic())
                         {
                             *first_unparsed = expression_cstr;
-                            *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+                            *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchSyntheticChild;
                             *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
                             return ValueObjectSP();
                         }
@@ -4080,3 +4080,9 @@
     }
     return m_format;
 }
+
+bool
+ValueObject::CanProvideValue ()
+{
+    return (false == GetClangType().IsAggregateType());
+}
diff --git a/lldb/source/Core/ValueObjectCast.cpp b/lldb/source/Core/ValueObjectCast.cpp
index 4f4f8cc..b20371b 100644
--- a/lldb/source/Core/ValueObjectCast.cpp
+++ b/lldb/source/Core/ValueObjectCast.cpp
@@ -102,7 +102,7 @@
         //m_value.SetContext (Value::eContextTypeClangType, clang_type);
         m_value.SetClangType (clang_type);
         SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren());
-        if (clang_type.IsAggregateType ())
+        if (!CanProvideValue())
         {
             // this value object represents an aggregate type whose
             // children have values, but this object does not. So we
diff --git a/lldb/source/Core/ValueObjectDynamicValue.cpp b/lldb/source/Core/ValueObjectDynamicValue.cpp
index 3481bde..30a42f0 100644
--- a/lldb/source/Core/ValueObjectDynamicValue.cpp
+++ b/lldb/source/Core/ValueObjectDynamicValue.cpp
@@ -329,7 +329,7 @@
         m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get());
         if (m_error.Success())
         {
-            if (GetClangType().IsAggregateType ())
+            if (!CanProvideValue())
             {
                 // this value object represents an aggregate type whose
                 // children have values, but this object does not. So we
diff --git a/lldb/source/Core/ValueObjectMemory.cpp b/lldb/source/Core/ValueObjectMemory.cpp
index d2cbbfd..5fbe87b 100644
--- a/lldb/source/Core/ValueObjectMemory.cpp
+++ b/lldb/source/Core/ValueObjectMemory.cpp
@@ -233,7 +233,7 @@
                 }
             }
 
-            if (GetClangType().IsAggregateType())
+            if (!CanProvideValue())
             {
                 // this value object represents an aggregate type whose
                 // children have values, but this object does not. So we
diff --git a/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/lldb/source/Core/ValueObjectSyntheticFilter.cpp
index 18d3616..9bdf689 100644
--- a/lldb/source/Core/ValueObjectSyntheticFilter.cpp
+++ b/lldb/source/Core/ValueObjectSyntheticFilter.cpp
@@ -66,16 +66,17 @@
     m_name_toindex(),
     m_synthetic_children_count(UINT32_MAX),
     m_parent_type_name(parent.GetTypeName()),
-    m_might_have_children(eLazyBoolCalculate)
+    m_might_have_children(eLazyBoolCalculate),
+    m_provides_value(eLazyBoolCalculate)
 {
-#ifdef LLDB_CONFIGURATION_DEBUG
+#ifdef FOOBAR
     std::string new_name(parent.GetName().AsCString());
     new_name += "$$__synth__";
     SetName (ConstString(new_name.c_str()));
 #else
     SetName(parent.GetName());
 #endif
-    CopyParentData();
+    CopyValueData(m_parent);
     CreateSynthFilter();
 }
 
@@ -191,7 +192,20 @@
         m_might_have_children = eLazyBoolCalculate;
     }
     
-    CopyParentData();
+    m_provides_value = eLazyBoolCalculate;
+    
+    lldb::ValueObjectSP synth_val(m_synth_filter_ap->GetSyntheticValue());
+    
+    if (synth_val && synth_val->CanProvideValue())
+    {
+        m_provides_value = eLazyBoolYes;
+        CopyValueData(synth_val.get());
+    }
+    else
+    {
+        m_provides_value = eLazyBoolNo;
+        CopyValueData(m_parent);
+    }
     
     SetValueIsValid(true);
     return true;
@@ -268,9 +282,19 @@
 }
 
 void
-ValueObjectSynthetic::CopyParentData ()
+ValueObjectSynthetic::CopyValueData (ValueObject *source)
 {
-    m_value = m_parent->GetValue();
+    m_value = (source->UpdateValueIfNeeded(), source->GetValue());
     ExecutionContext exe_ctx (GetExecutionContextRef());
     m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get());
 }
+
+bool
+ValueObjectSynthetic::CanProvideValue ()
+{
+    if (!UpdateValueIfNeeded())
+        return false;
+    if (m_provides_value == eLazyBoolYes)
+        return true;
+    return m_parent->CanProvideValue();
+}
diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp
index 225dc02..aa8ade1 100644
--- a/lldb/source/Core/ValueObjectVariable.cpp
+++ b/lldb/source/Core/ValueObjectVariable.cpp
@@ -234,7 +234,7 @@
                     }
                 }
 
-                if (GetClangType().IsAggregateType())
+                if (!CanProvideValue())
                 {
                     // this value object represents an aggregate type whose
                     // children have values, but this object does not. So we
diff --git a/lldb/source/DataFormatters/TypeFormat.cpp b/lldb/source/DataFormatters/TypeFormat.cpp
index 0c62daf..64d3750 100644
--- a/lldb/source/DataFormatters/TypeFormat.cpp
+++ b/lldb/source/DataFormatters/TypeFormat.cpp
@@ -59,9 +59,9 @@
 {
     if (!valobj)
         return false;
-    if (valobj->GetClangType().IsAggregateType () == false)
+    if (valobj->CanProvideValue())
     {
-        const Value& value(valobj->GetValue());
+        Value& value(valobj->GetValue());
         const Value::ContextType context_type = value.GetContextType();
         ExecutionContext exe_ctx (valobj->GetExecutionContextRef());
         DataExtractor data;
@@ -92,7 +92,7 @@
         }
         else
         {
-            ClangASTType clang_type = valobj->GetClangType ();
+            ClangASTType clang_type = value.GetClangType ();
             if (clang_type)
             {
                 // put custom bytes to display in the DataExtractor to override the default value logic
@@ -180,7 +180,7 @@
     dest.clear();
     if (!valobj)
         return false;
-    if (valobj->GetClangType().IsAggregateType ())
+    if (!valobj->CanProvideValue())
         return false;
     ProcessSP process_sp;
     TargetSP target_sp;
diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
index 3ce5051..126ff78 100644
--- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp
+++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
@@ -66,7 +66,7 @@
 bool
 ValueObjectPrinter::PrintValueObject ()
 {
-    if (!GetDynamicValueIfNeeded () || m_valobj == nullptr)
+    if (!GetMostSpecializedValue () || m_valobj == nullptr)
         return false;
     
     if (ShouldPrintValueObject())
@@ -97,7 +97,7 @@
 }
 
 bool
-ValueObjectPrinter::GetDynamicValueIfNeeded ()
+ValueObjectPrinter::GetMostSpecializedValue ()
 {
     if (m_valobj)
         return true;
@@ -134,6 +134,25 @@
             else
                 m_valobj = m_orig_valobj;
         }
+        
+        if (m_valobj->IsSynthetic())
+        {
+            if (options.m_use_synthetic == false)
+            {
+                ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
+                if (non_synthetic)
+                    m_valobj = non_synthetic;
+            }
+        }
+        else
+        {
+            if (options.m_use_synthetic == true)
+            {
+                ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
+                if (synthetic)
+                    m_valobj = synthetic;
+            }
+        }
     }
     m_clang_type = m_valobj->GetClangType();
     m_type_flags = m_clang_type.GetTypeInfo ();
@@ -442,8 +461,7 @@
 ValueObject*
 ValueObjectPrinter::GetValueObjectForChildrenGeneration ()
 {
-    ValueObjectSP synth_valobj_sp = m_valobj->GetSyntheticValue (options.m_use_synthetic);
-    return (synth_valobj_sp ? synth_valobj_sp.get() : m_valobj);
+    return m_valobj;
 }
 
 void
@@ -540,7 +558,13 @@
     {
         // Aggregate, no children...
         if (ShouldPrintValueObject())
-            m_stream->PutCString(" {}\n");
+        {
+            // if it has a synthetic value, then don't print {}, the synthetic children are probably only being used to vend a value
+            if (m_valobj->DoesProvideSyntheticValue())
+                m_stream->PutCString( "\n");
+            else
+                m_stream->PutCString(" {}\n");
+        }
     }
     else
     {
@@ -552,7 +576,7 @@
 bool
 ValueObjectPrinter::PrintChildrenOneLiner (bool hide_names)
 {
-    if (!GetDynamicValueIfNeeded () || m_valobj == nullptr)
+    if (!GetMostSpecializedValue () || m_valobj == nullptr)
         return false;
     
     ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration();
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 721eedb..87bfc95 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -124,6 +124,7 @@
                                           SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue,
                                           SWIGPythonUpdateSynthProviderInstance swig_update_provider,
                                           SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider,
+                                          SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider,
                                           SWIGPythonCallCommand swig_call_command,
                                           SWIGPythonCallModuleInit swig_call_module_init,
                                           SWIGPythonCreateOSPlugin swig_create_os_plugin,
@@ -148,6 +149,7 @@
                                                     swig_get_valobj_sp_from_sbvalue,
                                                     swig_update_provider,
                                                     swig_mighthavechildren_provider,
+                                                    swig_getvalue_provider,
                                                     swig_call_command,
                                                     swig_call_module_init,
                                                     swig_create_os_plugin,
diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp
index 03b39d1..987a168 100644
--- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp
@@ -55,6 +55,7 @@
 static ScriptInterpreter::SWIGPythonGetValueObjectSPFromSBValue g_swig_get_valobj_sp_from_sbvalue = nullptr;
 static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = nullptr;
 static ScriptInterpreter::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = nullptr;
+static ScriptInterpreter::SWIGPythonGetValueSynthProviderInstance g_swig_getvalue_provider = nullptr;
 static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = nullptr;
 static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = nullptr;
 static ScriptInterpreter::SWIGPythonCreateOSPlugin g_swig_create_os_plugin = nullptr;
@@ -2140,6 +2141,42 @@
     return ret_val;
 }
 
+lldb::ValueObjectSP
+ScriptInterpreterPython::GetSyntheticValue (const lldb::ScriptInterpreterObjectSP& implementor_sp)
+{
+    lldb::ValueObjectSP ret_val(nullptr);
+    
+    if (!implementor_sp)
+        return ret_val;
+    
+    void* implementor = implementor_sp->GetObject();
+    
+    if (!implementor)
+        return ret_val;
+    
+    if (!g_swig_getvalue_provider || !g_swig_cast_to_sbvalue || !g_swig_get_valobj_sp_from_sbvalue)
+        return ret_val;
+    
+    {
+        Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+        void* child_ptr = g_swig_getvalue_provider (implementor);
+        if (child_ptr != nullptr && child_ptr != Py_None)
+        {
+            lldb::SBValue* sb_value_ptr = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr);
+            if (sb_value_ptr == nullptr)
+                Py_XDECREF(child_ptr);
+            else
+                ret_val = g_swig_get_valobj_sp_from_sbvalue (sb_value_ptr);
+        }
+        else
+        {
+            Py_XDECREF(child_ptr);
+        }
+    }
+    
+    return ret_val;
+}
+
 static std::string
 ReadPythonBacktrace (PyObject* py_backtrace)
 {
@@ -2616,6 +2653,7 @@
                                                 SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue,
                                                 SWIGPythonUpdateSynthProviderInstance swig_update_provider,
                                                 SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider,
+                                                SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider,
                                                 SWIGPythonCallCommand swig_call_command,
                                                 SWIGPythonCallModuleInit swig_call_module_init,
                                                 SWIGPythonCreateOSPlugin swig_create_os_plugin,
@@ -2639,6 +2677,7 @@
     g_swig_get_valobj_sp_from_sbvalue = swig_get_valobj_sp_from_sbvalue;
     g_swig_update_provider = swig_update_provider;
     g_swig_mighthavechildren_provider = swig_mighthavechildren_provider;
+    g_swig_getvalue_provider = swig_getvalue_provider;
     g_swig_call_command = swig_call_command;
     g_swig_call_module_init = swig_call_module_init;
     g_swig_create_os_plugin = swig_create_os_plugin;
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py b/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py
index 9c3bed1..36ff60b 100644
--- a/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py
+++ b/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py
@@ -14,12 +14,14 @@
 
     @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
     @dsym_test
+    @unittest2.expectedFailure("rdar://15630776 - Summary cannot reference non-synthetic children if synthetic children exist")
     def test_with_dsym_and_run_command(self):
         """Test data formatter commands."""
         self.buildDsym()
         self.data_formatter_commands()
 
     @dwarf_test
+    @unittest2.expectedFailure("rdar://15630776 - Summary cannot reference non-synthetic children if synthetic children exist")
     def test_with_dwarf_and_run_command(self):
         """Test data formatter commands."""
         self.buildDwarf()
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synthval/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-synthval/Makefile
new file mode 100644
index 0000000..314f1cb
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/data-formatter-synthval/Makefile
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py b/lldb/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py
new file mode 100644
index 0000000..c5ff06b
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py
@@ -0,0 +1,95 @@
+"""
+Test lldb data formatter subsystem.
+"""
+
+import os, time
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+class DataFormatterSynthValueTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+    @dsym_test
+    def test_with_dsym_and_run_command(self):
+        """Test using Python synthetic children provider to provide a value."""
+        self.buildDsym()
+        self.data_formatter_commands()
+
+    @skipIfFreeBSD # llvm.org/pr20545 bogus output confuses buildbot parser
+    @dwarf_test
+    def test_with_dwarf_and_run_command(self):
+        """Test using Python synthetic children provider to provide a value."""
+        self.buildDwarf()
+        self.data_formatter_commands()
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line number to break at.
+        self.line = line_number('main.cpp', 'break here')
+
+    def data_formatter_commands(self):
+        """Test using Python synthetic children provider to provide a value."""
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        # The stop reason of the thread should be breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       'stop reason = breakpoint'])
+
+        # This is the function to remove the custom formats in order to have a
+        # clean slate for the next test case.
+        def cleanup():
+            self.runCmd('type format clear', check=False)
+            self.runCmd('type summary clear', check=False)
+            self.runCmd('type filter clear', check=False)
+            self.runCmd('type synth clear', check=False)
+
+        # Execute the cleanup function during test case tear down.
+        self.addTearDownHook(cleanup)
+        
+        x = self.frame().FindVariable("x")
+        x.SetPreferSyntheticValue(True)
+        y = self.frame().FindVariable("y")
+        y.SetPreferSyntheticValue(True)
+        z = self.frame().FindVariable("z")
+        z.SetPreferSyntheticValue(True)
+
+        x_val = x.GetValueAsUnsigned
+        y_val = y.GetValueAsUnsigned
+        z_val = z.GetValueAsUnsigned
+        
+        if self.TraceOn():
+            print "x_val = %s; y_val = %s; z_val = %s" % (x_val(),y_val(),z_val())
+
+        self.assertFalse(x_val() == 3, "x == 3 before synthetics")
+        self.assertFalse(y_val() == 4, "y == 4 before synthetics")
+        self.assertFalse(z_val() == 7, "z == 7 before synthetics")
+
+        # now set up the synth
+        self.runCmd("script from myIntSynthProvider import *")
+        self.runCmd("type synth add -l myIntSynthProvider myInt")
+        
+        if self.TraceOn():
+            print "x_val = %s; y_val = %s; z_val = %s" % (x_val(),y_val(),z_val())
+        
+        self.assertTrue(x_val() == 3, "x != 3 after synthetics")
+        self.assertTrue(y_val() == 4, "y != 4 after synthetics")
+        self.assertTrue(z_val() == 7, "z != 7 after synthetics")
+        
+        self.expect("frame variable x", substrs=['3'])
+        self.expect("frame variable x", substrs=['theValue = 3'], matching=False)
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synthval/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-synthval/main.cpp
new file mode 100644
index 0000000..fef128c
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/data-formatter-synthval/main.cpp
@@ -0,0 +1,15 @@
+class myInt {
+    private: int theValue;
+    public: myInt() : theValue(0) {}
+    public: myInt(int _x) : theValue(_x) {}
+    int val() { return theValue; }
+};
+
+myInt operator + (myInt x, myInt y) { return myInt(x.val() + y.val()); }
+
+int main() {
+    myInt x{3};
+    myInt y{4};
+    myInt z {x+y};
+    return z.val(); // break here
+}
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py b/lldb/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py
new file mode 100644
index 0000000..d37cb0ec
--- /dev/null
+++ b/lldb/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py
@@ -0,0 +1,17 @@
+class myIntSynthProvider(object):
+	def __init__(self, valobj, dict):
+		self.valobj = valobj;
+		self.val = self.valobj.GetChildMemberWithName("theValue")
+	def num_children(self):
+		return 0;
+	def get_child_at_index(self, index):
+	    return None
+	def get_child_index(self, name):
+	    return None
+	def update(self):
+		return False
+	def might_have_children(self):
+	    return False
+	def get_value(self):
+	    return self.val
+