jrtc27 created this revision.
jrtc27 added reviewers: arichardson, jdoerfert.
jrtc27 requested review of this revision.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.

ClassTemplateSpecializationDecl not within a ClassTemplateDecl
represents an explicit instatiation of a template and so should be
handled as if it were a normal CXXRecordDecl. Unfortunately, having an
equivalent for FunctionTemplateDecl remains a TODO in ASTDumper's
VisitFunctionTemplateDecl, with all the explicit instantiations just
being emitted inside the FunctionTemplateDecl along with all the other
specializations, meaning we can't easily support explicit function
instantiations in update_cc_test_checks.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D106243

Files:
  
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
  
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
  clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
  llvm/utils/update_cc_test_checks.py

Index: llvm/utils/update_cc_test_checks.py
===================================================================
--- llvm/utils/update_cc_test_checks.py
+++ llvm/utils/update_cc_test_checks.py
@@ -33,8 +33,8 @@
     '%clangxx': ['--driver-mode=g++'],
 }
 
-def get_line2spell_and_mangled(args, clang_args):
-  ret = {}
+def get_line2func_list(args, clang_args):
+  ret = collections.defaultdict(list)
   # Use clang's JSON AST dump to get the mangled name
   json_dump_args = [args.clang] + clang_args + ['-fsyntax-only', '-o', '-']
   if '-cc1' not in json_dump_args:
@@ -55,26 +55,37 @@
 
   # Parse the clang JSON and add all children of type FunctionDecl.
   # TODO: Should we add checks for global variables being emitted?
-  def parse_clang_ast_json(node):
+  def parse_clang_ast_json(node, loc, search):
     node_kind = node['kind']
     # Recurse for the following nodes that can contain nested function decls:
     if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl',
-                     'CXXRecordDecl'):
+                     'CXXRecordDecl', 'ClassTemplateSpecializationDecl'):
+      # Specializations must use the loc from the specialization, not the
+      # template, and search for the class's spelling as the specialization
+      # does not mention the method names in the source.
+      if node_kind == 'ClassTemplateSpecializationDecl':
+        inner_loc = node['loc']
+        inner_search = node['name']
+      else:
+        inner_loc = None
+        inner_search = None
       if 'inner' in node:
         for inner in node['inner']:
-          parse_clang_ast_json(inner)
+          parse_clang_ast_json(inner, inner_loc, inner_search)
     # Otherwise we ignore everything except functions:
     if node_kind not in ('FunctionDecl', 'CXXMethodDecl', 'CXXConstructorDecl',
                          'CXXDestructorDecl', 'CXXConversionDecl'):
       return
+    if loc is None:
+      loc = node['loc']
     if node.get('isImplicit') is True and node.get('storageClass') == 'extern':
-      common.debug('Skipping builtin function:', node['name'], '@', node['loc'])
+      common.debug('Skipping builtin function:', node['name'], '@', loc)
       return
-    common.debug('Found function:', node['kind'], node['name'], '@', node['loc'])
-    line = node['loc'].get('line')
+    common.debug('Found function:', node['kind'], node['name'], '@', loc)
+    line = loc.get('line')
     # If there is no line it is probably a builtin function -> skip
     if line is None:
-      common.debug('Skipping function without line number:', node['name'], '@', node['loc'])
+      common.debug('Skipping function without line number:', node['name'], '@', loc)
       return
 
     # If there is no 'inner' object, it is a function declaration and we can
@@ -88,20 +99,23 @@
           has_body = True
           break
     if not has_body:
-      common.debug('Skipping function without body:', node['name'], '@', node['loc'])
+      common.debug('Skipping function without body:', node['name'], '@', loc)
       return
     spell = node['name']
+    if search is None:
+      search = spell
     mangled = node.get('mangledName', spell)
-    ret[int(line)-1] = (spell, mangled)
+    ret[int(line)-1].append((spell, mangled, search))
 
   ast = json.loads(stdout)
   if ast['kind'] != 'TranslationUnitDecl':
     common.error('Clang AST dump JSON format changed?')
     sys.exit(2)
-  parse_clang_ast_json(ast)
+  parse_clang_ast_json(ast, None, None)
 
-  for line, func_name in sorted(ret.items()):
-    common.debug('line {}: found function {}'.format(line+1, func_name), file=sys.stderr)
+  for line, funcs in sorted(ret.items()):
+    for func in funcs:
+      common.debug('line {}: found function {}'.format(line+1, func), file=sys.stderr)
   if not ret:
     common.warn('Did not find any functions using', ' '.join(json_dump_args))
   return ret
@@ -222,7 +236,7 @@
                              comment_prefix='//', argparse_callback=infer_dependent_args):
     # Build a list of filechecked and non-filechecked RUN lines.
     run_list = []
-    line2spell_and_mangled_list = collections.defaultdict(list)
+    line2func_list = collections.defaultdict(list)
 
     subs = {
       '%s' : ti.path,
@@ -296,8 +310,8 @@
 
       # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
       # mangled names. Forward all clang args for now.
-      for k, v in get_line2spell_and_mangled(ti.args, clang_args).items():
-        line2spell_and_mangled_list[k].append(v)
+      for k, v in get_line2func_list(ti.args, clang_args).items():
+        line2func_list[k].extend(v)
 
     func_dict = builder.finish_and_get_func_dict()
     global_vars_seen_dict = {}
@@ -357,15 +371,16 @@
         # Skip special separator comments added by commmon.add_global_checks.
         if line.strip() == '//' + common.SEPARATOR:
           continue
-        if idx in line2spell_and_mangled_list:
+        if idx in line2func_list:
           added = set()
-          for spell, mangled in line2spell_and_mangled_list[idx]:
+          for spell, mangled, search in line2func_list[idx]:
             # One line may contain multiple function declarations.
             # Skip if the mangled name has been added before.
-            # The line number may come from an included file,
-            # we simply require the spelling name to appear on the line
-            # to exclude functions from other files.
-            if mangled in added or spell not in line:
+            # The line number may come from an included file, we simply require
+            # the search string (normally the function's spelling name, but is
+            # the class's spelling name for class specializations) to appear on
+            # the line to exclude functions from other files.
+            if mangled in added or search not in line:
               continue
             if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
               last_line = output_lines[-1].strip()
Index: clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
@@ -0,0 +1,10 @@
+## Test that CHECK lines are generated for explicit template instantiatons
+
+# RUN: cp %S/Inputs/explicit-template-instantiation.cpp %t.cpp && %update_cc_test_checks %t.cpp
+# RUN: diff -u %S/Inputs/explicit-template-instantiation.cpp.expected %t.cpp
+## Check that it also works with the --llvm-bin flag instead of --clang
+# RUN: cp %S/Inputs/explicit-template-instantiation.cpp %t.cpp && %update_cc_test_checks --clang='' --llvm-bin=%clang_tools_dir %t.cpp
+# RUN: diff -u %S/Inputs/explicit-template-instantiation.cpp.expected %t.cpp
+## Check that re-running update_cc_test_checks doesn't change the output
+# RUN: %update_cc_test_checks %t.cpp
+# RUN: diff -u %S/Inputs/explicit-template-instantiation.cpp.expected %t.cpp
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
@@ -0,0 +1,190 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+struct Foo {
+private:
+  T x;
+
+public:
+  Foo(T x) : x(x) {}
+  ~Foo() {}
+
+  T get() { return x; }
+  void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+  struct Foo<T> foo;
+
+public:
+  Bar(T x) : foo(x) {}
+  ~Bar() {}
+
+  T get() { return foo.get(); }
+  void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+  Baz(T x) : Foo<T>(x) {}
+  ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+// CHECK-LABEL: @_ZN3FooIcEC1Ec(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i8 [[X:%.*]], i8* [[X_ADDR]], align 1
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[X_ADDR]], align 1
+// CHECK-NEXT:    call void @_ZN3FooIcEC2Ec(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]], i8 signext [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIcED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3FooIcED2Ev(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]]) #[[ATTR2:[0-9]+]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIcE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[X]], align 1
+// CHECK-NEXT:    ret i8 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIcE3setEc(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i8 [[_X:%.*]], i8* [[_X_ADDR]], align 1
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[_X_ADDR]], align 1
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    store i8 [[TMP0]], i8* [[X]], align 1
+// CHECK-NEXT:    ret void
+//
+template struct Foo<char>;
+
+// CHECK-LABEL: @_ZN3FooIsEC1Es(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i16 [[X:%.*]], i16* [[X_ADDR]], align 2
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[X_ADDR]], align 2
+// CHECK-NEXT:    call void @_ZN3FooIsEC2Es(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]], i16 signext [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIsED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3FooIsED2Ev(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIsE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[X]], align 2
+// CHECK-NEXT:    ret i16 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIsE3setEs(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i16 [[_X:%.*]], i16* [[_X_ADDR]], align 2
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[_X_ADDR]], align 2
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    store i16 [[TMP0]], i16* [[X]], align 2
+// CHECK-NEXT:    ret void
+//
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+// CHECK-LABEL: @_ZN3BarIiEC1Ei(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i32 [[X:%.*]], i32* [[X_ADDR]], align 4
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, i32* [[X_ADDR]], align 4
+// CHECK-NEXT:    call void @_ZN3BarIiEC2Ei(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]], i32 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BarIiED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BarIiED2Ev(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BarIiE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 @_ZN3FooIiE3getEv(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]])
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+// CHECK-LABEL: @_ZN3BarIiE3setEi(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i32 [[_X:%.*]], i32* [[_X_ADDR]], align 4
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, i32* [[_X_ADDR]], align 4
+// CHECK-NEXT:    call void @_ZN3FooIiE3setEi(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]], i32 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+// CHECK-LABEL: @_ZN3BazIlEC1El(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[X:%.*]], i64* [[X_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, i64* [[X_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BazIlEC2El(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]], i64 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BazIlED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT:    store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BazIlED2Ev(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+template struct Baz<long>;
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+struct Foo {
+private:
+  T x;
+
+public:
+  Foo(T x) : x(x) {}
+  ~Foo() {}
+
+  T get() { return x; }
+  void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+  struct Foo<T> foo;
+
+public:
+  Bar(T x) : foo(x) {}
+  ~Bar() {}
+
+  T get() { return foo.get(); }
+  void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+  Baz(T x) : Foo<T>(x) {}
+  ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+template struct Foo<char>;
+
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+template struct Baz<long>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to