+2021-04-14  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
+
+       * dwarf2/read.c (inherit_abstract_dies): Keep a reference to the
+       corresponding child of the abstract DIE when iterating the
+       children of the concrete DIE.
+
 2021-04-13  Tom de Vries  <tdevries@suse.de>
 
        * ui-style.c (read_semi_number, extended_color): Change idx parameter
 
               sect_offset_str (die->sect_off),
               sect_offset_str (origin_die->sect_off));
 
+  /* Find if the concrete and abstract trees are structurally the
+     same.  This is a shallow traversal and it is not bullet-proof;
+     the compiler can trick the debugger into believing that the trees
+     are isomorphic, whereas they actually are not.  However, the
+     likelyhood of this happening is pretty low, and a full-fledged
+     check would be an overkill.  */
+  bool are_isomorphic = true;
+  die_info *concrete_child = die->child;
+  die_info *abstract_child = origin_die->child;
+  while (concrete_child != nullptr || abstract_child != nullptr)
+    {
+      if (concrete_child == nullptr
+         || abstract_child == nullptr
+         || concrete_child->tag != abstract_child->tag)
+       {
+         are_isomorphic = false;
+         break;
+       }
+
+      concrete_child = concrete_child->sibling;
+      abstract_child = abstract_child->sibling;
+    }
+
+  /* Walk the origin's children in parallel to the concrete children.
+     This helps match an origin child in case the debug info misses
+     DW_AT_abstract_origin attributes.  Keep in mind that the abstract
+     origin tree may not have the same tree structure as the concrete
+     DIE, though.  */
+  die_info *corresponding_abstract_child
+    = are_isomorphic ? origin_die->child : nullptr;
+
   std::vector<sect_offset> offsets;
 
   for (child_die = die->child;
         one.  */
       if (child_die->tag == DW_TAG_call_site
          || child_die->tag == DW_TAG_GNU_call_site)
-       continue;
+       {
+         if (are_isomorphic)
+           corresponding_abstract_child
+             = corresponding_abstract_child->sibling;
+         continue;
+       }
 
       /* For each CHILD_DIE, find the corresponding child of
         ORIGIN_DIE.  If there is more than one layer of
                                             &child_origin_cu);
        }
 
+      /* If missing DW_AT_abstract_origin, try the corresponding child
+        of the origin.  Clang emits such lexical scopes.  */
+      if (child_origin_die == child_die
+         && dwarf2_attr (child_die, DW_AT_abstract_origin, cu) == nullptr
+         && are_isomorphic
+         && child_die->tag == DW_TAG_lexical_block)
+       child_origin_die = corresponding_abstract_child;
+
       /* According to DWARF3 3.3.8.2 #3 new entries without their abstract
         counterpart may exist.  */
       if (child_origin_die != child_die)
          else
            offsets.push_back (child_origin_die->sect_off);
        }
+
+      if (are_isomorphic)
+       corresponding_abstract_child = corresponding_abstract_child->sibling;
     }
   std::sort (offsets.begin (), offsets.end ());
   sect_offset *offsets_end = offsets.data () + offsets.size ();
 
+2021-04-14  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
+
+       * gdb.opt/inline-locals.c (scoped): New function.
+       (main): Call 'scoped'.
+       * gdb.opt/inline-locals.exp: Update with "info locals" tests
+       for scoped variables.
+       * gdb.dwarf2/dw2-inline-with-lexical-scope.c: New file.
+       * gdb.dwarf2/dw2-inline-with-lexical-scope.exp: New file.
+
 2021-04-14  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
 
        * lib/dwarf.exp (_location): Recognize DW_OP_fbreg as an op.
 
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 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/>.  */
+
+#ifdef __GNUC__
+#define ATTR __attribute__((always_inline))
+#else
+#define ATTR
+#endif
+
+int global_num = 0;
+int global_value = 0;
+
+inline ATTR
+static void
+func ()
+{ /* func prologue */
+  global_num = 42;
+  int num= 42;
+  if (num > 2)
+    {
+      asm ("scope_label1: .globl scope_label1");
+      global_value = num;
+      int value = num;
+      asm ("breakpoint_label: .globl breakpoint_label");
+      value += 10;
+      asm ("scope_label2: .globl scope_label2");
+    }
+} /* func end */
+
+int
+main ()
+{ /* main prologue */
+  asm ("main_label: .globl main_label");
+  func (); /* func call */
+  asm ("main_label2: .globl main_label2");
+  return 0; /* main return */
+} /* main end */
 
--- /dev/null
+# Copyright 2021 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/>.
+
+# Test that scoped local variables in an inlined function are printed
+# properly.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets that support DWARF-2 and use
+# gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile .c .S
+
+# Make some DWARF for the test.  The concrete inlined instance
+# (i.e. the DW_TAG_inlined_subroutine) has a DW_TAG_lexical_block that
+# does not contain a DW_AT_abstract_origin attribute.  This is
+# deliberate.  Bad GDB printed duplicate local variables with
+# "optimized out" values in this case.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcfile srcdir subdir
+    declare_labels int_label func_label num_label value_label lines_label
+
+    get_func_info main
+    set func_call [gdb_get_line_number "func call"]
+
+    set global_num_addr [gdb_target_symbol global_num]
+    set global_value_addr [gdb_target_symbol global_value]
+
+    cu {} {
+       compile_unit {
+           {language @DW_LANG_C99}
+           {name $srcfile}
+           {low_pc $main_start addr}
+           {high_pc "$main_start + $main_len" addr}
+           {stmt_list ${lines_label} DW_FORM_sec_offset}
+       } {
+           int_label: base_type {
+               {name "int"}
+               {byte_size 4 sdata}
+               {encoding @DW_ATE_signed}
+           }
+
+           func_label: subprogram {
+               {name func}
+               {inline 3 data1}
+           } {
+               num_label: DW_TAG_variable {
+                   {name num}
+                   {type :$int_label}
+               }
+               lexical_block {
+               } {
+                   value_label: DW_TAG_variable {
+                       {name value}
+                       {type :$int_label}
+                   }
+               }
+           }
+
+           subprogram {
+               {name main}
+               {external 1 flag}
+               {low_pc $main_start addr}
+               {high_pc "$main_start + $main_len" addr}
+           } {
+               inlined_subroutine {
+                   {abstract_origin %$func_label}
+                   {low_pc main_label addr}
+                   {high_pc main_label2 addr}
+                   {call_file 1 data1}
+                   {call_line $func_call data1}
+               } {
+                   DW_TAG_variable {
+                       {abstract_origin %$num_label}
+                       {location {addr $global_num_addr} SPECIAL_expr}
+                   }
+                   lexical_block {
+                       {low_pc scope_label1 addr}
+                       {high_pc scope_label2 addr}
+                   } {
+                       DW_TAG_variable {
+                           {abstract_origin %$value_label}
+                           {location {addr $global_value_addr} SPECIAL_expr}
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    lines {version 2} lines_label {
+       include_dir "${srcdir}/${subdir}"
+       file_name "$srcfile" 1
+
+       program {
+           {DW_LNE_set_address $main_start}
+           {line [gdb_get_line_number "main prologue"]}
+           {DW_LNS_copy}
+           {DW_LNE_set_address main_label}
+           {line [gdb_get_line_number "func call"]}
+           {DW_LNS_copy}
+           {DW_LNE_set_address main_label}
+           {line [gdb_get_line_number "func end"]}
+           {DW_LNS_copy}
+           {DW_LNE_set_address main_label2}
+           {line [gdb_get_line_number "main end"]}
+           {DW_LNS_copy}
+           {DW_LNE_set_address $main_end}
+           {DW_LNE_end_sequence}
+       }
+    }
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} \
+        [list $srcfile $asm_file] {nodebug}]} {
+    return -1
+}
+
+runto breakpoint_label
+
+# Bad GDB was printing an additional "value = <optimized out>".
+gdb_test "info locals" "value = 42\r\nnum = 42"
 
   return x * func1 (arg2);
 }
 
+inline ATTR
+void
+scoped (int s)
+{
+  int loc1 = 10;
+  if (s > 0)
+    {
+      int loc2 = 20;
+      s++; /* bp for locals 1 */
+      if (s > 1)
+       {
+         int loc3 = 30;
+         s++; /* bp for locals 2 */
+       }
+    }
+  s++; /* bp for locals 3 */
+}
+
 int main (void)
 {
   int val;
   val = func2 (result);
   result = val;
 
+  scoped (40);
+
   return 0;
 }
 
 }
 
 gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local 3"
+
+# Test printing scoped local variables.
+
+proc check_scoped_locals {bp_label pass_re} {
+    global srcfile
+
+    set locals_bp [gdb_get_line_number $bp_label ${srcfile}]
+    gdb_breakpoint $srcfile:$locals_bp
+
+    gdb_continue_to_breakpoint "$bp_label" ".*$srcfile:$locals_bp.*"
+    set kfail_re [multi_line $pass_re ".*<optimized out>"]
+    gdb_test_multiple "info locals" "scoped info locals at $bp_label" {
+       -re -wrap $pass_re {
+           pass $gdb_test_name
+       }
+       -re -wrap $kfail_re {
+           if {[test_compiler_info {gcc-[0-8]-*-*}]} {
+               kfail gdb/25695 $gdb_test_name
+           } else {
+               fail $gdb_test_name
+           }
+       }
+    }
+}
+
+if {! $no_frames } {
+    check_scoped_locals "bp for locals 1" "loc2 = 20\r\nloc1 = 10"
+    check_scoped_locals "bp for locals 2" "loc3 = 30\r\nloc2 = 20\r\nloc1 = 10"
+    check_scoped_locals "bp for locals 3" "loc1 = 10"
+}