diff --git a/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp b/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp new file mode 100644 --- /dev/null +++ b/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 +struct Foo { +private: + T x; + +public: + Foo(T x) : x(x) {} + ~Foo() {} + + T get() { return x; } + void set(T _x) { x = _x; } +}; + +template +struct Bar { +private: + struct Foo foo; + +public: + Bar(T x) : foo(x) {} + ~Bar() {} + + T get() { return foo.get(); } + void set(T _x) { foo.set(_x); } +}; + +template +struct Baz : Foo { +public: + Baz(T x) : Foo(x) {} + ~Baz() {} +}; + +// These two specializations should generate lines for all of Foo's methods. + +template struct Foo; + +template struct Foo; + +// This should not generate lines for the implicit specialization of Foo, but +// should generate lines for the explicit specialization of Bar. + +template struct Bar; + +// This should not generate lines for the implicit specialization of Foo, but +// should generate lines for the explicit specialization of Baz. + +template struct Baz; diff --git a/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected b/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected new file mode 100644 --- /dev/null +++ b/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 +struct Foo { +private: + T x; + +public: + Foo(T x) : x(x) {} + ~Foo() {} + + T get() { return x; } + void set(T _x) { x = _x; } +}; + +template +struct Bar { +private: + struct Foo foo; + +public: + Bar(T x) : foo(x) {} + ~Bar() {} + + T get() { return foo.get(); } + void set(T _x) { foo.set(_x); } +}; + +template +struct Baz : Foo { +public: + Baz(T x) : Foo(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; + +// 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; + +// 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; + +// 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; diff --git a/clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test b/clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test @@ -0,0 +1,7 @@ +## 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 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 diff --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py --- a/llvm/utils/update_cc_test_checks.py +++ b/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()