unittests/environ-selftests.c \
        unittests/filtered_iterator-selftests.c \
        unittests/format_pieces-selftests.c \
+       unittests/frame_info_ptr-selftests.c \
        unittests/function-view-selftests.c \
        unittests/gdb_tilde_expand-selftests.c \
        unittests/gmp-utils-selftests.c \
 
 {
   m_cached_level = frame_relative_level (*this);
 
-  if (m_cached_level != 0)
+  if (m_cached_level != 0
+      || (m_ptr != nullptr && m_ptr->this_id.value.user_created_p))
     m_cached_id = get_frame_id (*this);
 }
 
       return;
     }
 
-  /* Frame #0 needs special handling, see comment in select_frame.  */
-  if (m_cached_level == 0)
-    m_ptr = get_current_frame ().get ();
+  if (m_cached_id.user_created_p)
+    m_ptr = create_new_frame (m_cached_id).get ();
   else
     {
-      gdb_assert (frame_id_p (m_cached_id));
-      m_ptr = frame_find_by_id (m_cached_id).get ();
+      /* Frame #0 needs special handling, see comment in select_frame.  */
+      if (m_cached_level == 0)
+       m_ptr = get_current_frame ().get ();
+      else
+       {
+         /* If we reach here without a valid frame id, it means we are trying
+            to reinflate a frame whose id was not know at construction time.
+            We're probably trying to reinflate a frame while computing its id
+            which is not possible, and would indicate a problem with GDB.  */
+         gdb_assert (frame_id_p (m_cached_id));
+         m_ptr = frame_find_by_id (m_cached_id).get ();
+       }
     }
 
   gdb_assert (m_ptr != nullptr);
 
   /* The underlying pointer.  */
   frame_info *m_ptr = nullptr;
 
-  /* The frame_id of the underlying pointer.  */
+  /* The frame_id of the underlying pointer.
+
+     For the current target frames (frames with level 0, obtained through
+     get_current_frame), we don't save the frame id, we leave it at
+     null_frame_id.  For user-created frames (also with level 0, but created
+     with create_new_frame), we do save the id.  */
   frame_id m_cached_id = null_frame_id;
 
   /* The frame level of the underlying pointer.  */
 
   int n;
 };
 
+__attribute__((used)) static int
+called_from_pretty_printer (void)
+{
+  return 23;
+}
+
 static int
 baz (struct type_1 z1, struct type_2 z2)
 {
 
     return
 }
 
-proc test_select_frame_view {} {
+# If WITH_PRETTY_PRINTER is true, load pretty printers for the function
+# parameter types, in which we do an inferior call.  This is meant to test
+# that the frame_info_ptr correctly reinflates frames created using
+# "select-frame view".
+
+proc test_select_frame_view { with_pretty_printer } {
     clean_restart $::binfile
 
+    if { $with_pretty_printer } {
+       require allow_python_tests
+    }
+
     if { ![runto_main] } {
        return
     }
 
+    if { $with_pretty_printer } {
+       set remote_python_file \
+           [gdb_remote_download host "${::srcdir}/${::subdir}/${::testfile}.py"]
+       gdb_test_no_output "source ${remote_python_file}" "load python file"
+    }
+
     # Stop thread 2 at a baz.
     gdb_test "break baz"
     gdb_test "continue" "Thread 2.*hit Breakpoint $::decimal, baz .*"
     gdb_test "thread 1" "Switching to thread 1 .*"
     gdb_test_no_output "select-frame view $frame_sp $frame_pc"
 
+    if { $with_pretty_printer } {
+       # When the pretty printer does its infcall, it is done on the currently
+       # selected thread, thread 1 here.  However, other threads are resumed
+       # at the same time.  This causes thread 2 to exit during that infcall,
+       # leading to this weirdness:
+       #
+       #     frame^M
+       #     #0  baz (z=[Thread 0x7ffff7cc26c0 (LWP 417519) exited]^M
+       #     hohoho) at /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/frame-view.c:35^M
+       #     35        return z.n + 2;^M
+       #
+       # Avoid that by setting scheduler-locking on.
+       gdb_test_no_output "set scheduler-locking on"
+
+       set z1_pattern "hahaha"
+       set z2_pattern "hohoho"
+    } else {
+       set z1_pattern "\\.\\.\\."
+       set z2_pattern "\\.\\.\\."
+    }
+
     # Verify that the "frame" command does not change the selected frame.
     # There used to be a bug where the "frame" command would lose the
     # selection of user-created frames.
-    gdb_test "frame" "#0  baz \\(z1=.*, z2=.*\\).*" "frame"
-    gdb_test "frame" "#0  baz \\(z1=.*, z2=.*\\).*" "frame again"
+    gdb_test "frame" "#0  baz \\(z1=$z1_pattern, z2=$z2_pattern\\).*" "frame"
+    gdb_test "frame" "#0  baz \\(z1=$z1_pattern, z2=$z2_pattern\\).*" "frame again"
 }
 
-test_select_frame_view
+foreach_with_prefix with_pretty_printer {false true} {
+    test_select_frame_view $with_pretty_printer
+}
 
--- /dev/null
+# Copyright (C) 2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+class Printer1:
+    def to_string(self):
+        n = gdb.parse_and_eval("called_from_pretty_printer ()")
+        assert n == 23
+        return "hahaha"
+
+
+class Printer2:
+    def to_string(self):
+        n = gdb.parse_and_eval("called_from_pretty_printer ()")
+        assert n == 23
+        return "hohoho"
+
+
+def lookup_function(val):
+    if str(val.type) == "struct type_1":
+        return Printer1()
+
+    if str(val.type) == "struct type_2":
+        return Printer2()
+
+    return None
+
+
+gdb.pretty_printers.append(lookup_function)
 
--- /dev/null
+/* Self tests for frame_info_ptr.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "frame.h"
+#include "gdbsupport/selftest.h"
+#include "scoped-mock-context.h"
+#include "test-target.h"
+
+namespace selftests {
+
+static void
+validate_user_created_frame (frame_id id)
+{
+  SELF_CHECK (id.stack_status == FID_STACK_VALID);
+  SELF_CHECK (id.stack_addr == 0x1234);
+  SELF_CHECK (id.code_addr_p);
+  SELF_CHECK (id.code_addr == 0x5678);
+}
+
+static frame_info_ptr
+user_created_frame_callee (frame_info_ptr frame)
+{
+  validate_user_created_frame (get_frame_id (frame));
+
+  frame.prepare_reinflate ();
+  reinit_frame_cache ();
+  frame.reinflate ();
+
+  validate_user_created_frame (get_frame_id (frame));
+
+  return frame;
+}
+
+static void
+test_user_created_frame ()
+{
+  scoped_mock_context<test_target_ops> mock_context
+    (current_inferior ()->gdbarch);
+  frame_info_ptr frame = create_new_frame (0x1234, 0x5678);
+
+  validate_user_created_frame (get_frame_id (frame));
+
+  /* Pass the frame to a callee, which calls reinit_frame_cache.  This lets us
+     validate that the reinflation in both the callee and caller restore the
+     same frame_info object.  */
+  frame.prepare_reinflate ();
+  frame_info_ptr callees_frame_info = user_created_frame_callee (frame);
+  frame.reinflate ();
+
+  validate_user_created_frame (get_frame_id (frame));
+  SELF_CHECK (frame.get () == callees_frame_info.get ());
+}
+
+} /* namespace selftests */
+
+void _initialize_frame_info_ptr_selftests ();
+void
+_initialize_frame_info_ptr_selftests ()
+{
+  selftests::register_test ("frame_info_ptr_user",
+                           selftests::test_user_created_frame);
+}