diff options
author | Jason Merrill <jason@redhat.com> | 2009-08-10 16:47:55 -0400 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2009-08-10 16:47:55 -0400 |
commit | e8f43da6f9d34f17f3e6e4adbf42e79addb5e217 (patch) | |
tree | a4546f632af854bec30da3734e864ea5f11269f0 /gcc | |
parent | 8d1b99e26ad5c88a05c7c7ba2fbb05bdc345e9d2 (diff) | |
download | gcc-e8f43da6f9d34f17f3e6e4adbf42e79addb5e217.zip gcc-e8f43da6f9d34f17f3e6e4adbf42e79addb5e217.tar.gz gcc-e8f43da6f9d34f17f3e6e4adbf42e79addb5e217.tar.bz2 |
Implement DR 757...
Implement DR 757: It's OK for a decl to use a type without linkage
so long as the decl is defined in the current translation unit.
* decl2.c (no_linkage_decls): New vector.
(mark_used): Add decls that use types with no linkage.
(cp_write_global_declarations): Check that they are defined.
(decl_defined_p, no_linkage_error): New fns.
* cp-tree.h (DECL_NO_LINKAGE_CHECKED): New macro.
(struct lang_decl_base): Add flag.
* decl.c (grokfndecl): Don't check type linkage.
(grokvardecl): If the type has no linkage, just make sure
DECL_LANG_SPECIFIC is set.
* pt.c (check_instantiated_arg): Don't check type linkage.
* name-lookup.c (is_local_extern): New fn.
* name-lookup.h: Declare it.
From-SVN: r150634
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/ChangeLog | 17 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 11 | ||||
-rw-r--r-- | gcc/cp/decl.c | 67 | ||||
-rw-r--r-- | gcc/cp/decl2.c | 70 | ||||
-rw-r--r-- | gcc/cp/name-lookup.c | 28 | ||||
-rw-r--r-- | gcc/cp/name-lookup.h | 1 | ||||
-rw-r--r-- | gcc/cp/pt.c | 27 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 15 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/anon-struct4.C | 1 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/lookup/anon2.C | 6 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/other/anon3.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/other/linkage2.C | 31 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/arg2.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/local4.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.old-deja/g++.law/operators32.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.old-deja/g++.other/anon9.C | 5 | ||||
-rw-r--r-- | gcc/testsuite/g++.old-deja/g++.other/linkage1.C | 13 | ||||
-rw-r--r-- | gcc/testsuite/g++.old-deja/g++.other/linkage2.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.old-deja/g++.pt/enum6.C | 2 |
19 files changed, 204 insertions, 102 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a7cc6b1..dc9f1ca 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,20 @@ +2009-08-10 Jason Merrill <jason@redhat.com> + + Implement DR 757: It's OK for a decl to use a type without linkage + so long as the decl is defined in the current translation unit. + * decl2.c (no_linkage_decls): New vector. + (mark_used): Add decls that use types with no linkage. + (cp_write_global_declarations): Check that they are defined. + (decl_defined_p, no_linkage_error): New fns. + * cp-tree.h (DECL_NO_LINKAGE_CHECKED): New macro. + (struct lang_decl_base): Add flag. + * decl.c (grokfndecl): Don't check type linkage. + (grokvardecl): If the type has no linkage, just make sure + DECL_LANG_SPECIFIC is set. + * pt.c (check_instantiated_arg): Don't check type linkage. + * name-lookup.c (is_local_extern): New fn. + * name-lookup.h: Declare it. + 2009-08-05 Jason Merrill <jason@redhat.com> PR c++/40948 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c507ac8..ae39110 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1579,8 +1579,9 @@ struct GTY(()) lang_decl_base { unsigned anticipated_p : 1; /* fn or type */ unsigned friend_attr : 1; /* fn or type */ unsigned template_conv_p : 1; /* template only? */ + unsigned no_linkage_checked : 1; /* var or fn */ unsigned u2sel : 1; - /* 2 spare bits */ + /* 1 spare bit */ }; /* True for DECL codes which have template info and access. */ @@ -1982,6 +1983,14 @@ struct GTY(()) lang_decl { (DECL_LANG_SPECIFIC (VAR_OR_FUNCTION_DECL_CHECK (DECL)) \ ->u.base.initialized_in_class) +/* Nonzero if we've checked whether DECL uses types without linkage in a + potentially invalid way. + ??? Instead, should fix mark_used to only set TREE_USED when we're + really using something, and just return if it's already set. */ +#define DECL_NO_LINKAGE_CHECKED(DECL) \ + (DECL_LANG_SPECIFIC (VAR_OR_FUNCTION_DECL_CHECK (DECL)) \ + ->u.base.no_linkage_checked) + /* Nonzero for DECL means that this decl is just a friend declaration, and should not be added to the list of members for this class. */ #define DECL_FRIEND_P(NODE) (DECL_LANG_SPECIFIC (NODE)->u.base.friend_attr) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index be1b5b7..898542f 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6747,36 +6747,6 @@ grokfndecl (tree ctype, || decl_function_context (TYPE_MAIN_DECL (ctype)))) publicp = 0; - if (publicp) - { - /* [basic.link]: A name with no linkage (notably, the name of a class - or enumeration declared in a local scope) shall not be used to - declare an entity with linkage. - - Only check this for public decls for now. See core 319, 389. */ - t = no_linkage_check (TREE_TYPE (decl), - /*relaxed_p=*/false); - if (t) - { - if (TYPE_ANONYMOUS_P (t)) - { - if (DECL_EXTERN_C_P (decl)) - /* Allow this; it's pretty common in C. */; - else - { - permerror (input_location, "non-local function %q#D uses anonymous type", - decl); - if (DECL_ORIGINAL_TYPE (TYPE_NAME (t))) - permerror (input_location, "%q+#D does not refer to the unqualified " - "type, so it is not used for linkage", - TYPE_NAME (t)); - } - } - else - permerror (input_location, "non-local function %q#D uses local type %qT", decl, t); - } - } - TREE_PUBLIC (decl) = publicp; if (! publicp) { @@ -7021,36 +6991,13 @@ grokvardecl (tree type, if (TREE_PUBLIC (decl)) { - /* [basic.link]: A name with no linkage (notably, the name of a class - or enumeration declared in a local scope) shall not be used to - declare an entity with linkage. - - Only check this for public decls for now. */ - tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false); - if (t) - { - if (TYPE_ANONYMOUS_P (t)) - { - if (DECL_EXTERN_C_P (decl)) - /* Allow this; it's pretty common in C. */ - ; - else - { - /* DRs 132, 319 and 389 seem to indicate types with - no linkage can only be used to declare extern "C" - entities. Since it's not always an error in the - ISO C++ 90 Standard, we only issue a warning. */ - warning (0, "non-local variable %q#D uses anonymous type", - decl); - if (DECL_ORIGINAL_TYPE (TYPE_NAME (t))) - warning (0, "%q+#D does not refer to the unqualified " - "type, so it is not used for linkage", - TYPE_NAME (t)); - } - } - else - warning (0, "non-local variable %q#D uses local type %qT", decl, t); - } + /* If the type of the decl has no linkage, make sure that we'll + notice that in mark_used. */ + if (DECL_LANG_SPECIFIC (decl) == NULL + && TREE_PUBLIC (decl) + && !DECL_EXTERN_C_P (decl) + && no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false)) + retrofit_lang_decl (decl); } else DECL_INTERFACE_KNOWN (decl) = 1; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index df79e9c..610d62d2 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -84,6 +84,7 @@ static void write_out_vars (tree); static void import_export_class (tree); static tree get_guard_bits (tree); static void determine_visibility_from_class (tree, tree); +static bool decl_defined_p (tree); /* A list of static class variables. This is needed, because a static class variable can be declared inside the class without @@ -94,6 +95,10 @@ static GTY(()) VEC(tree,gc) *pending_statics; may need to emit outline anyway. */ static GTY(()) VEC(tree,gc) *deferred_fns; +/* A list of decls that use types with no linkage, which we need to make + sure are defined. */ +static GTY(()) VEC(tree,gc) *no_linkage_decls; + /* Nonzero if we're done parsing and into end-of-file activities. */ int at_eof; @@ -3332,6 +3337,40 @@ build_java_method_aliases (void) } } +/* Returns true iff there is a definition available for variable or + function DECL. */ + +static bool +decl_defined_p (tree decl) +{ + if (TREE_CODE (decl) == FUNCTION_DECL) + return (DECL_INITIAL (decl) != NULL_TREE); + else + { + gcc_assert (TREE_CODE (decl) == VAR_DECL); + return !DECL_EXTERNAL (decl); + } +} + +/* Complain that DECL uses a type with no linkage but is never defined. */ + +static void +no_linkage_error (tree decl) +{ + tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false); + if (TYPE_ANONYMOUS_P (t)) + { + permerror (0, "%q+#D, declared using anonymous type, " + "is used but never defined", decl); + if (is_typedef_decl (TYPE_NAME (t))) + permerror (0, "%q+#D does not refer to the unqualified type, " + "so it is not used for linkage", TYPE_NAME (t)); + } + else + permerror (0, "%q+#D, declared using local type %qT, " + "is used but never defined", decl, t); +} + /* This routine is called at the end of compilation. Its job is to create all the code needed to initialize and destroy the global aggregates. We do the destruction @@ -3613,6 +3652,11 @@ cp_write_global_declarations (void) } } + /* So must decls that use a type with no linkage. */ + for (i = 0; VEC_iterate (tree, no_linkage_decls, i, decl); ++i) + if (!decl_defined_p (decl)) + no_linkage_error (decl); + /* We give C linkage to static constructors and destructors. */ push_lang_context (lang_name_c); @@ -3851,6 +3895,32 @@ mark_used (tree decl) if (processing_template_decl) return; + /* DR 757: A type without linkage shall not be used as the type of a + variable or function with linkage, unless + o the variable or function has extern "C" linkage (7.5 [dcl.link]), or + o the variable or function is not used (3.2 [basic.def.odr]) or is + defined in the same translation unit. */ + if (TREE_PUBLIC (decl) + && (TREE_CODE (decl) == FUNCTION_DECL + || TREE_CODE (decl) == VAR_DECL) + && DECL_LANG_SPECIFIC (decl) + && !DECL_NO_LINKAGE_CHECKED (decl)) + { + DECL_NO_LINKAGE_CHECKED (decl) = true; + if (!DECL_EXTERN_C_P (decl) + && !DECL_ARTIFICIAL (decl) + && !decl_defined_p (decl) + && no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false)) + { + if (is_local_extern (decl)) + /* There's no way to define a local extern, and adding it to + the vector interferes with GC, so give an error now. */ + no_linkage_error (decl); + else + VEC_safe_push (tree, gc, no_linkage_decls, decl); + } + } + if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl) && !TREE_ASM_WRITTEN (decl)) /* Remember it, so we can check it was defined. */ diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index c2d8779..feb2cf2 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -4392,6 +4392,34 @@ lookup_name_innermost_nonclass_level (tree name) POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t); } +/* Returns true iff DECL is a block-scope extern declaration of a function + or variable. */ + +bool +is_local_extern (tree decl) +{ + cxx_binding *binding; + + /* For functions, this is easy. */ + if (TREE_CODE (decl) == FUNCTION_DECL) + return DECL_LOCAL_FUNCTION_P (decl); + + if (TREE_CODE (decl) != VAR_DECL) + return false; + if (!current_function_decl) + return false; + + /* For variables, this is not easy. We need to look at the binding stack + for the identifier to see whether the decl we have is a local. */ + for (binding = IDENTIFIER_BINDING (DECL_NAME (decl)); + binding && binding->scope->kind != sk_namespace; + binding = binding->previous) + if (binding->value == decl) + return LOCAL_BINDING_P (binding); + + return false; +} + /* Like lookup_name_innermost_nonclass_level, but for types. */ static tree diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 2203a84..7a3625a 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -318,6 +318,7 @@ extern tree remove_hidden_names (tree); extern tree lookup_qualified_name (tree, tree, bool, bool); extern tree lookup_name_nonclass (tree); extern tree lookup_name_innermost_nonclass_level (tree); +extern bool is_local_extern (tree); extern tree lookup_function_nonclass (tree, VEC(tree,gc) *, bool); extern void push_local_binding (tree, tree, int); extern bool pushdecl_class_level (tree); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index c0c61c5..36f1b00 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12215,7 +12215,7 @@ tsubst_copy_and_build (tree t, } /* Verify that the instantiated ARGS are valid. For type arguments, - make sure that the type's linkage is ok. For non-type arguments, + make sure that the type is not variably modified. For non-type arguments, make sure they are constants if they are integral or enumerations. Emit an error under control of COMPLAIN, and return TRUE on error. */ @@ -12236,30 +12236,7 @@ check_instantiated_arg (tree tmpl, tree t, tsubst_flags_t complain) } else if (TYPE_P (t)) { - /* [basic.link]: A name with no linkage (notably, the name - of a class or enumeration declared in a local scope) - shall not be used to declare an entity with linkage. - This implies that names with no linkage cannot be used as - template arguments. */ - tree nt = no_linkage_check (t, /*relaxed_p=*/false); - - if (nt) - { - /* DR 488 makes use of a type with no linkage cause - type deduction to fail. */ - if (complain & tf_error) - { - if (TYPE_ANONYMOUS_P (nt)) - error ("%qT is/uses anonymous type", t); - else - error ("template argument for %qD uses local type %qT", - tmpl, t); - } - return true; - } - /* In order to avoid all sorts of complications, we do not - allow variably-modified types as template arguments. */ - else if (variably_modified_type_p (t, NULL_TREE)) + if (variably_modified_type_p (t, NULL_TREE)) { if (complain & tf_error) error ("%qT is a variably modified type", t); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2f7c1b2..bea2eaa 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2009-08-10 Jason Merrill <jason@redhat.com> + + * g++.dg/other/linkage2.C: New test for types-without-linkage + handling. + * g++.dg/ext/anon-struct4.C: No error about anonymous type. + * g++.dg/lookup/anon2.C: Likewise. + * g++.dg/other/anon3.C: Likewise. + * g++.dg/template/arg2.C: Likewise. + * g++.dg/template/local4.C: Likewise. + * g++.old-deja/g++.law/operators32.C: Likewise. + * g++.old-deja/g++.other/linkage2.C: Likewise. + * g++.old-deja/g++.pt/enum6.C: Likewise. + * g++.old-deja/g++.other/anon9.C: Use the undefined decls. + * g++.old-deja/g++.other/linkage1.C: Likewise. + 2009-08-10 Manuel López-Ibáñez <manu@gcc.gnu.org> * gcc.dg/dg.exp: Test also c-c++-common dir. diff --git a/gcc/testsuite/g++.dg/ext/anon-struct4.C b/gcc/testsuite/g++.dg/ext/anon-struct4.C index 4f0fcd1..53302d8 100644 --- a/gcc/testsuite/g++.dg/ext/anon-struct4.C +++ b/gcc/testsuite/g++.dg/ext/anon-struct4.C @@ -1,4 +1,3 @@ // PR c++/14401 struct { struct { int& i ; } bar ; } foo ; // { dg-error "uninitialized" "uninit" } -// { dg-warning "anonymous" "anon" { target *-*-* } 3 } diff --git a/gcc/testsuite/g++.dg/lookup/anon2.C b/gcc/testsuite/g++.dg/lookup/anon2.C index d556ba0..3143b62 100644 --- a/gcc/testsuite/g++.dg/lookup/anon2.C +++ b/gcc/testsuite/g++.dg/lookup/anon2.C @@ -1,9 +1,9 @@ // { dg-do compile } // { dg-options "" } -// Make sure we issue a diagnostic if a type with no linkage is used -// to declare a a variable that has linkage. +// Make sure we don't issue a diagnostic if a type with no linkage is used +// to declare a a variable that has linkage if that variable is defined. -struct { int i; } a; // { dg-warning "anonymous type" } +struct { int i; } a; void foo() { a.i; } diff --git a/gcc/testsuite/g++.dg/other/anon3.C b/gcc/testsuite/g++.dg/other/anon3.C index 87116eb..87cbfb5 100644 --- a/gcc/testsuite/g++.dg/other/anon3.C +++ b/gcc/testsuite/g++.dg/other/anon3.C @@ -4,4 +4,4 @@ // { dg-do compile } -enum { a = 3 } x; // { dg-warning "anonymous type" } +enum { a = 3 } x; diff --git a/gcc/testsuite/g++.dg/other/linkage2.C b/gcc/testsuite/g++.dg/other/linkage2.C new file mode 100644 index 0000000..4e3e6f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/other/linkage2.C @@ -0,0 +1,31 @@ +// DR 743: A type without linkage shall not be used as the type of a +// variable or function with linkage, unless +// o the variable or function has extern "C" linkage (7.5 [dcl.link]), or +// o the variable or function is not used (3.2 [basic.def.odr]) or is +// defined in the same translation unit. + +template <typename T> struct B { + void g(T){} + void h(T); // { dg-error "never defined" } + friend void i(B, T){} + static T t1; // { dg-error "never defined" } + static T t2; +}; + +template <typename T> T B<T>::t2 = { }; + +enum {} e1; // OK, defined +extern enum {} e2; // { dg-error "never defined" } +extern "C" enum {} e3; // OK, extern "C" + +void f() { + struct A { int x; }; // no linkage + A a = {1}; + B<A> ba; // declares B<A>::g(A) and B<A>::h(A) + ba.t1 = a; // error, B<T>::t never defined + ba.t2 = a; // OK + ba.g(a); // OK + ba.h(a); // error, B<T>::h never defined + i(ba, a); // OK + e1+e2+e3; +} diff --git a/gcc/testsuite/g++.dg/template/arg2.C b/gcc/testsuite/g++.dg/template/arg2.C index 9fb7a68..1314b25 100644 --- a/gcc/testsuite/g++.dg/template/arg2.C +++ b/gcc/testsuite/g++.dg/template/arg2.C @@ -10,5 +10,5 @@ template <typename T> class X {}; void fn () { class L {}; - X<L> f; // { dg-error "uses local type|trying to instantiate|no type|invalid type" "" } + X<L> f; } diff --git a/gcc/testsuite/g++.dg/template/local4.C b/gcc/testsuite/g++.dg/template/local4.C index cfa3736..41e2370 100644 --- a/gcc/testsuite/g++.dg/template/local4.C +++ b/gcc/testsuite/g++.dg/template/local4.C @@ -4,5 +4,5 @@ template <typename T> void foo() {} int main () { struct S {}; - foo<S> (); // { dg-error "match" } + foo<S> (); } diff --git a/gcc/testsuite/g++.old-deja/g++.law/operators32.C b/gcc/testsuite/g++.old-deja/g++.law/operators32.C index 91de03e..89f0b66 100644 --- a/gcc/testsuite/g++.old-deja/g++.law/operators32.C +++ b/gcc/testsuite/g++.old-deja/g++.law/operators32.C @@ -49,7 +49,7 @@ foo() {std::cout << "foo created" << std::endl; } }; foo **f2; -allocate2d(d1, d2, f2);// { dg-error "" } type.*// ERROR - trying to.* -ffree(d1, f2);// { dg-error "" } type.*// ERROR - trying to.* +allocate2d(d1, d2, f2); +ffree(d1, f2); } diff --git a/gcc/testsuite/g++.old-deja/g++.other/anon9.C b/gcc/testsuite/g++.old-deja/g++.other/anon9.C index a364db8..f4b1923 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/anon9.C +++ b/gcc/testsuite/g++.old-deja/g++.other/anon9.C @@ -4,3 +4,8 @@ typedef const struct { int i; } T; // { dg-error "" } referenced below void f (T* t); // { dg-error "" } uses unnamed type + +int main() +{ + f(0); +} diff --git a/gcc/testsuite/g++.old-deja/g++.other/linkage1.C b/gcc/testsuite/g++.old-deja/g++.other/linkage1.C index e9b5a9d..de9a6ac 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/linkage1.C +++ b/gcc/testsuite/g++.old-deja/g++.other/linkage1.C @@ -3,13 +3,16 @@ typedef struct { int i; } *p; -void f (p) { } // { dg-error "uses anonymous type" } -p q; // { dg-warning "uses anonymous type" } +void f (p) { } +p q; int main() { - extern p j; // { dg-warning "uses anonymous type" } + extern p j; // { dg-error "anonymous type" } + j+1; struct A { int j; }; - extern A a; // { dg-warning "uses local type" } - extern void f (A); // { dg-error "uses local type" } + extern A a; // { dg-error "local type" } + a.j+1; + extern void f (A); // { dg-error "local type" } + f(a); } diff --git a/gcc/testsuite/g++.old-deja/g++.other/linkage2.C b/gcc/testsuite/g++.old-deja/g++.other/linkage2.C index 2385b22..64f74f7 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/linkage2.C +++ b/gcc/testsuite/g++.old-deja/g++.other/linkage2.C @@ -7,7 +7,7 @@ extern GDBM_FILE gdbm_open(); } typedef struct { int dummy[10]; } *FAIL_FILE; -extern FAIL_FILE fail_open(); // { dg-error "" } non-local function +extern FAIL_FILE fail_open(); // OK because it's never used typedef struct { int dummy[10]; } *SUCCESS_FILE, S; extern SUCCESS_FILE success_open(); diff --git a/gcc/testsuite/g++.old-deja/g++.pt/enum6.C b/gcc/testsuite/g++.old-deja/g++.pt/enum6.C index 254b48b..561254d 100644 --- a/gcc/testsuite/g++.old-deja/g++.pt/enum6.C +++ b/gcc/testsuite/g++.old-deja/g++.pt/enum6.C @@ -8,7 +8,7 @@ void fn(T) { enum tern { H, L, X, U }; - vector<tern> ternvec; // { dg-error "" } composed from a local type + vector<tern> ternvec; } template void fn(int); |