Implement P0386R2 - C++17 inline variables
authorJason Merrill <jason@gcc.gnu.org>
Thu, 13 Oct 2016 19:26:54 +0000 (15:26 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 13 Oct 2016 19:26:54 +0000 (15:26 -0400)
2016-10-11  Jakub Jelinek  <jakub@redhat.com>

* dwarf2out.c (gen_member_die): Handle inline static data member
definitions.
c-family/
* c-cppbuiltin.c (c_cpp_builtins): Add __cpp_inline_variables.
cp/
* cp-tree.h (struct lang_type): Shrink language field to 1 bit
from 4.  Add var_declared_inline_p field.  Mention 2 spare bits.
(DECL_VAR_DECLARED_INLINE_P): Define.
(SET_DECL_VAR_DECLARED_INLINE_P): Define.
(DECL_INLINE_VAR_P): Define.
(diagnose_inline_vars_for_namespace): Declare.
* decl.c (diagnose_inline_vars_for_namespace): New function.
(duplicate_decls): For static data members copy
DECL_DECLARED_CONSTEXPR_P.
(redeclaration_error_message): Handle C++17 redundant redeclaration
of constexpr static data member outside of class.
(maybe_commonize_var): Handle inline variables.
(check_initializer): Ignore inline variables for diagnostics.
Adjust diagnostic wording for C++17.
(make_rtl_for_nonlocal_decl): Allow in-class definition of
inline static data members.
(bad_specifiers): Don't diagnose inline on variables here.
(grokvardecl): Add inlinep argument, non-static const inline variables
are TREE_PUBLIC.
(check_static_variable_definition): Return early also for inline
variables.
(grokdeclarator): Handle inline variables and inline static data
members.
* typeck2.c (store_init_value): Don't diagnose non-constant
initializers for non-constexpr inline static data members.
* decl2.c (vague_linkage_p): Return true for inline variables.
(c_parse_final_cleanups): In-class declaration of inline static
data members is a definition.  Call diagnose_inline_vars_for_namespace
through walk_namespaces.
* pt.c (instantiate_decl): Set pattern_defined for in-class definitions
of inline static data members.

From-SVN: r241137

20 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-cppbuiltin.c
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/pt.c
gcc/cp/typeck2.c
gcc/dwarf2out.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/decl-diagnose.C
gcc/testsuite/g++.dg/cpp0x/constexpr-ice10.C
gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
gcc/testsuite/g++.dg/cpp1z/inline-var1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/inline-var1.h [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/inline-var1a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/inline-var2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/inline-var3.C [new file with mode: 0644]
gcc/testsuite/g++.old-deja/g++.brendan/misc6.C

index 6fdf0b965770cc031d556febcd7af5de703ef580..ae5a7109a81dc2339406d7058014f8d76444276c 100644 (file)
@@ -1,3 +1,8 @@
+2016-10-13  Jakub Jelinek  <jakub@redhat.com>
+
+       * dwarf2out.c (gen_member_die): Handle inline static data member
+       definitions.
+
 2016-10-13  Nathan Sidwell  <nathan@acm.org>
 
        * gcov-io.c (gcov_open): Fix documentation.  Simplify setting
index 285ef04e6f175af44e5056c865de9763bdb3a86b..e4ba2de334d571d43e3e59b52971aaef7edc631f 100644 (file)
@@ -1,3 +1,7 @@
+2016-10-13  Jason Merrill  <jason@redhat.com>
+
+       * c-cppbuiltin.c (c_cpp_builtins): Add __cpp_inline_variables.
+
 2016-10-13  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * c-cppbuiltin.c: Include memmodel.h.
index 94af585eafedece18f588cc55201887fdd5b0894..06b5aa3ac6518028d4eccfcac84f898fae9e370e 100644 (file)
@@ -935,6 +935,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_constexpr=201603");
          cpp_define (pfile, "__cpp_if_constexpr=201606");
          cpp_define (pfile, "__cpp_capture_star_this=201603");
+         cpp_define (pfile, "__cpp_inline_variables=201606");
        }
       if (flag_concepts)
        /* Use a value smaller than the 201507 specified in
index d792fb4e79a9e619ef5fddd7ef8f704c00c0fc49..5753ab10a9a039d0546a76726d3e9d1efd1b0e4f 100644 (file)
@@ -1,3 +1,44 @@
+2016-10-13  Jakub Jelinek  <jakub@redhat.com>
+           Jason Merrill  <jason@redhat.com>
+
+       Implement P0386R2 - C++17 inline variables
+       * cp-tree.h (struct lang_type): Shrink language field to 1 bit
+       from 4.  Add var_declared_inline_p field.  Mention 2 spare bits.
+       (DECL_VAR_DECLARED_INLINE_P): Define.
+       (SET_DECL_VAR_DECLARED_INLINE_P): Define.
+       (DECL_INLINE_VAR_P): Define.
+       (diagnose_inline_vars_for_namespace): Declare.
+       * decl.c (diagnose_inline_vars_for_namespace): New function.
+       (duplicate_decls): For static data members copy
+       DECL_DECLARED_CONSTEXPR_P.
+       (redeclaration_error_message): Handle C++17 redundant redeclaration
+       of constexpr static data member outside of class.
+       (maybe_commonize_var): Handle inline variables.
+       (check_initializer): Ignore inline variables for diagnostics.
+       Adjust diagnostic wording for C++17.
+       (make_rtl_for_nonlocal_decl): Allow in-class definition of
+       inline static data members.
+       (bad_specifiers): Don't diagnose inline on variables here.
+       (grokvardecl): Add inlinep argument, non-static const inline variables
+       are TREE_PUBLIC.
+       (check_static_variable_definition): Return early also for inline
+       variables.
+       (mark_inline_variable): New.
+       (grokdeclarator): Handle inline variables and inline static data
+       members.
+       * typeck2.c (store_init_value): Don't diagnose non-constant
+       initializers for non-constexpr inline static data members.
+       * decl2.c (vague_linkage_p): Return true for inline variables.
+       (c_parse_final_cleanups): In-class declaration of inline static
+       data members is a definition.  Call diagnose_inline_vars_for_namespace
+       through walk_namespaces.
+       * pt.c (instantiate_decl): Set pattern_defined for in-class definitions
+       of inline static data members.
+
+2016-10-13  Jason Merrill  <jason@redhat.com>
+
+       * decl.c (mark_inline_variable): New.
+
 2016-10-13  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * decl2.c: Include memmodel.h.
index 88cae04e69b44fa66d186ab9075162e54f00cdad..f4a8985b351121f3f6a54b44888308ee98244533 100644 (file)
@@ -2214,7 +2214,7 @@ struct GTY(()) lang_type {
 
 struct GTY(()) lang_decl_base {
   unsigned selector : 16;   /* Larger than necessary for faster access.  */
-  ENUM_BITFIELD(languages) language : 4;
+  ENUM_BITFIELD(languages) language : 1;
   unsigned use_template : 2;
   unsigned not_really_extern : 1;         /* var or fn */
   unsigned initialized_in_class : 1;      /* var or fn */
@@ -2227,7 +2227,8 @@ struct GTY(()) lang_decl_base {
   unsigned odr_used : 1;                  /* var or fn */
   unsigned u2sel : 1;
   unsigned concept_p : 1;                  /* applies to vars and functions */
-  /* 0 spare bits */
+  unsigned var_declared_inline_p : 1;     /* var */
+  /* 2 spare bits */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -3607,6 +3608,23 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CP_DECL_THREADPRIVATE_P(DECL) \
   (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (DECL))->u.base.threadprivate_or_deleted_p)
 
+/* Nonzero if NODE is a VAR_DECL which has been declared inline.  */
+#define DECL_VAR_DECLARED_INLINE_P(NODE) \
+  (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))                  \
+   ? DECL_LANG_SPECIFIC (NODE)->u.base.var_declared_inline_p   \
+   : false)
+#define SET_DECL_VAR_DECLARED_INLINE_P(NODE) \
+  (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.var_declared_inline_p \
+   = true)
+
+/* Nonzero if NODE is an inline VAR_DECL.  In C++17, static data members
+   declared with constexpr specifier are implicitly inline variables.  */
+#define DECL_INLINE_VAR_P(NODE) \
+  (DECL_VAR_DECLARED_INLINE_P (NODE)                           \
+   || (cxx_dialect >= cxx1z                                    \
+       && DECL_DECLARED_CONSTEXPR_P (NODE)                     \
+       && DECL_CLASS_SCOPE_P (NODE)))
+
 /* Nonzero if DECL was declared with '= delete'.  */
 #define DECL_DELETED_FN(DECL) \
   (LANG_DECL_FN_CHECK (DECL)->min.base.threadprivate_or_deleted_p)
@@ -5799,6 +5817,7 @@ typedef int (*walk_namespaces_fn)         (tree, void *);
 extern int walk_namespaces                     (walk_namespaces_fn,
                                                 void *);
 extern int wrapup_globals_for_namespace                (tree, void *);
+extern int diagnose_inline_vars_for_namespace  (tree, void *);
 extern tree create_implicit_typedef            (tree, tree);
 extern int local_variable_p                    (const_tree);
 extern tree register_dtor_fn                   (tree);
index 62408931900c5e3c13cf532beda72642fe531fbf..f761d0dfa92594414b06448f8c479347a0b9ddc3 100644 (file)
@@ -68,7 +68,7 @@ static int unary_op_p (enum tree_code);
 static void push_local_name (tree);
 static tree grok_reference_init (tree, tree, tree, int);
 static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *,
-                        int, int, int, tree);
+                        int, int, int, int, tree);
 static int check_static_variable_definition (tree, tree);
 static void record_unknown_type (tree, const char *);
 static tree builtin_function_1 (tree, tree, bool);
@@ -937,6 +937,27 @@ wrapup_globals_for_namespace (tree name_space, void* data ATTRIBUTE_UNUSED)
   /* Write out any globals that need to be output.  */
   return wrapup_global_declarations (vec, len);
 }
+
+/* Diagnose odr-used extern inline variables without definitions
+   in the current TU.  */
+int
+diagnose_inline_vars_for_namespace (tree name_space, void *)
+{
+  cp_binding_level *level = NAMESPACE_LEVEL (name_space);
+  vec<tree, va_gc> *statics = level->static_decls;
+  tree decl;
+  unsigned int i;
+
+  FOR_EACH_VEC_SAFE_ELT (statics, i, decl)
+    if (VAR_P (decl)
+       && DECL_EXTERNAL (decl)
+       && DECL_INLINE_VAR_P (decl)
+       && DECL_ODR_USED (decl))
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "odr-used inline variable %qD is not defined", decl);
+
+  return 0;
+}
 \f
 /* In C++, you don't have to write `struct S' to refer to `S'; you
    can just use `S'.  We accomplish this by creating a TYPE_DECL as
@@ -2098,6 +2119,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
            |= DECL_NONTRIVIALLY_INITIALIZED_P (olddecl);
          DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (newdecl)
            |= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (olddecl);
+          if (DECL_CLASS_SCOPE_P (olddecl))
+            DECL_DECLARED_CONSTEXPR_P (newdecl)
+             |= DECL_DECLARED_CONSTEXPR_P (olddecl);
 
          /* Merge the threadprivate attribute from OLDDECL into NEWDECL.  */
          if (DECL_LANG_SPECIFIC (olddecl)
@@ -2882,6 +2906,27 @@ redeclaration_error_message (tree newdecl, tree olddecl)
         is valid.  */
       if (DECL_EXTERNAL (newdecl) || DECL_EXTERNAL (olddecl))
        return NULL;
+
+      /* Static data member declared outside a class definition
+        if the variable is defined within the class with constexpr
+        specifier is declaration rather than definition (and
+        deprecated).  */
+      if (cxx_dialect >= cxx1z
+         && DECL_CLASS_SCOPE_P (olddecl)
+         && DECL_DECLARED_CONSTEXPR_P (olddecl)
+         && !DECL_INITIAL (newdecl))
+       {
+         DECL_EXTERNAL (newdecl) = 1;
+         /* For now, only warn with explicit -Wdeprecated.  */
+         if (global_options_set.x_warn_deprecated
+             && warning_at (DECL_SOURCE_LOCATION (newdecl), OPT_Wdeprecated,
+                            "redundant redeclaration of %<constexpr%> static "
+                            "data member %qD", newdecl))
+           inform (DECL_SOURCE_LOCATION (olddecl),
+                   "previous declaration of %qD", olddecl);
+         return NULL;
+       }
+
       /* Reject two definitions.  */
       return G_("redefinition of %q#D");
     }
@@ -5405,11 +5450,12 @@ maybe_commonize_var (tree decl)
 {
   /* Static data in a function with comdat linkage also has comdat
      linkage.  */
-  if (TREE_STATIC (decl)
-      /* Don't mess with __FUNCTION__.  */
-      && ! DECL_ARTIFICIAL (decl)
-      && DECL_FUNCTION_SCOPE_P (decl)
-      && vague_linkage_p (DECL_CONTEXT (decl)))
+  if ((TREE_STATIC (decl)
+       /* Don't mess with __FUNCTION__.  */
+       && ! DECL_ARTIFICIAL (decl)
+       && DECL_FUNCTION_SCOPE_P (decl)
+       && vague_linkage_p (DECL_CONTEXT (decl)))
+      || (TREE_PUBLIC (decl) && DECL_INLINE_VAR_P (decl)))
     {
       if (flag_weak)
        {
@@ -5435,10 +5481,17 @@ maybe_commonize_var (tree decl)
                 be merged.  */
              TREE_PUBLIC (decl) = 0;
              DECL_COMMON (decl) = 0;
+             const char *msg;
+             if (DECL_INLINE_VAR_P (decl))
+               msg = G_("sorry: semantics of inline variable "
+                        "%q#D are wrong (you%'ll wind up with "
+                        "multiple copies)");
+             else
+               msg = G_("sorry: semantics of inline function "
+                        "static data %q#D are wrong (you%'ll wind "
+                        "up with multiple copies)");
              if (warning_at (DECL_SOURCE_LOCATION (decl), 0,
-                             "sorry: semantics of inline function static "
-                             "data %q#D are wrong (you%'ll wind up "
-                             "with multiple copies)", decl))
+                             msg, decl))
                inform (DECL_SOURCE_LOCATION (decl),
                        "you can work around this by removing the initializer");
            }
@@ -6282,15 +6335,19 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
       TREE_CONSTANT (decl) = false;
     }
 
-  if (init_code && DECL_IN_AGGR_P (decl))
+  if (init_code
+      && (DECL_IN_AGGR_P (decl) && !DECL_VAR_DECLARED_INLINE_P (decl)))
     {
       static int explained = 0;
 
       if (cxx_dialect < cxx11)
        error ("initializer invalid for static member with constructor");
-      else
+      else if (cxx_dialect < cxx1z)
        error ("non-constant in-class initialization invalid for static "
               "member %qD", decl);
+      else
+       error ("non-constant in-class initialization invalid for non-inline "
+              "static member %qD", decl);
       if (!explained)
        {
          inform (input_location,
@@ -6346,7 +6403,9 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
       /* An in-class declaration of a static data member should be
         external; it is only a declaration, and not a definition.  */
       if (init == NULL_TREE)
-       gcc_assert (DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl));
+       gcc_assert (DECL_EXTERNAL (decl)
+                   || !TREE_PUBLIC (decl)
+                   || DECL_INLINE_VAR_P (decl));
     }
 
   /* We don't create any RTL for local variables.  */
@@ -7745,8 +7804,6 @@ bad_specifiers (tree object,
       case BSP_VAR:
        if (virtualp)
          error ("%qD declared as a %<virtual%> variable", object);
-       if (inlinep)
-         error ("%qD declared as an %<inline%> variable", object);
        if (quals)
          error ("%<const%> and %<volatile%> function specifiers on "
                 "%qD invalid in variable declaration", object);
@@ -8456,6 +8513,7 @@ grokvardecl (tree type,
             const cp_decl_specifier_seq *declspecs,
             int initialized,
             int flags,
+            int inlinep,
             int template_count,
             tree scope)
 {
@@ -8520,7 +8578,9 @@ grokvardecl (tree type,
   else if (toplevel_bindings_p ())
     {
       TREE_PUBLIC (decl) = (declspecs->storage_class != sc_static
-                           && (DECL_THIS_EXTERN (decl) || ! constp));
+                           && (DECL_THIS_EXTERN (decl)
+                               || ! constp
+                               || inlinep));
       TREE_STATIC (decl) = ! DECL_EXTERNAL (decl);
     }
   /* Not at top level, only `static' makes a static definition.  */
@@ -8692,8 +8752,10 @@ check_static_variable_definition (tree decl, tree type)
   if (dependent_type_p (type))
     return 0;
   /* If DECL is declared constexpr, we'll do the appropriate checks
-     in check_initializer.  */
-  if (DECL_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl))
+     in check_initializer.  Similarly for inline static data members.  */
+  if (DECL_P (decl)
+      && (DECL_DECLARED_CONSTEXPR_P (decl)
+         || DECL_VAR_DECLARED_INLINE_P (decl)))
     return 0;
   else if (cxx_dialect >= cxx11 && !INTEGRAL_OR_ENUMERATION_TYPE_P (type))
     {
@@ -9241,6 +9303,29 @@ check_var_type (tree identifier, tree type)
   return type;
 }
 
+/* Handle declaring DECL as an inline variable.  */
+
+static void
+mark_inline_variable (tree decl)
+{
+  bool inlinep = true;
+  if (! toplevel_bindings_p ())
+    {
+      error ("%<inline%> specifier invalid for variable "
+            "%qD declared at block scope", decl);
+      inlinep = false;
+    }
+  else if (cxx_dialect < cxx1z)
+    pedwarn (DECL_SOURCE_LOCATION (decl), 0,
+            "inline variables are only available "
+            "with -std=c++1z or -std=gnu++1z");
+  if (inlinep)
+    {
+      retrofit_lang_decl (decl);
+      SET_DECL_VAR_DECLARED_INLINE_P (decl);
+    }
+}
+
 /* Given declspecs and a declarator (abstract or otherwise), determine
    the name and type of the object declared and construct a DECL node
    for it.
@@ -11349,11 +11434,6 @@ grokdeclarator (const cp_declarator *declarator,
                                            : input_location,
                                            VAR_DECL, unqualified_id, type);
                set_linkage_for_static_data_member (decl);
-               /* Even if there is an in-class initialization, DECL
-                  is considered undefined until an out-of-class
-                  definition is provided.  */
-               DECL_EXTERNAL (decl) = 1;
-
                if (thread_p)
                  {
                    CP_DECL_THREAD_LOCAL_P (decl) = true;
@@ -11371,6 +11451,17 @@ grokdeclarator (const cp_declarator *declarator,
                           "initializer", decl);
                    constexpr_p = false;
                  }
+
+               if (inlinep)
+                 mark_inline_variable (decl);
+
+               if (!DECL_VAR_DECLARED_INLINE_P (decl)
+                   && !(cxx_dialect >= cxx1z && constexpr_p))
+                 /* Even if there is an in-class initialization, DECL
+                    is considered undefined until an out-of-class
+                    definition is provided, unless this is an inline
+                    variable.  */
+                 DECL_EXTERNAL (decl) = 1;
              }
            else
              {
@@ -11411,7 +11502,8 @@ grokdeclarator (const cp_declarator *declarator,
 
            bad_specifiers (decl, BSP_FIELD, virtualp,
                            memfn_quals != TYPE_UNQUALIFIED,
-                           inlinep, friendp, raises != NULL_TREE);
+                           staticp ? false : inlinep, friendp,
+                           raises != NULL_TREE);
          }
       }
     else if (TREE_CODE (type) == FUNCTION_TYPE
@@ -11535,6 +11627,7 @@ grokdeclarator (const cp_declarator *declarator,
                            declspecs,
                            initialized,
                            ((type_quals & TYPE_QUAL_CONST) != 0) | (2 * concept_p),
+                           inlinep,
                            template_count,
                            ctype ? ctype : in_namespace);
        if (decl == NULL_TREE)
@@ -11573,6 +11666,9 @@ grokdeclarator (const cp_declarator *declarator,
                   decl);
            constexpr_p = false;
          }
+
+       if (inlinep)
+         mark_inline_variable (decl);
       }
 
     if (VAR_P (decl) && !initialized)
index ea9d379989d6631b2af64071fd88db5ca7a9b03b..e0fff1e81c54e142243d40cdbc75d89ce91af0f8 100644 (file)
@@ -1827,7 +1827,8 @@ vague_linkage_p (tree decl)
       || (TREE_CODE (decl) == FUNCTION_DECL
          && DECL_DECLARED_INLINE_P (decl))
       || (DECL_LANG_SPECIFIC (decl)
-         && DECL_TEMPLATE_INSTANTIATION (decl)))
+         && DECL_TEMPLATE_INSTANTIATION (decl))
+      || (VAR_P (decl) && DECL_INLINE_VAR_P (decl)))
     return true;
   else if (DECL_FUNCTION_SCOPE_P (decl))
     /* A local static in an inline effectively has vague linkage.  */
@@ -4711,7 +4712,7 @@ c_parse_final_cleanups (void)
        {
          if (var_finalized_p (decl) || DECL_REALLY_EXTERN (decl)
              /* Don't write it out if we haven't seen a definition.  */
-             || DECL_IN_AGGR_P (decl))
+             || (DECL_IN_AGGR_P (decl) && !DECL_INLINE_VAR_P (decl)))
            continue;
          import_export_decl (decl);
          /* If this static data member is needed, provide it to the
@@ -4728,6 +4729,8 @@ c_parse_final_cleanups (void)
     }
   while (reconsider);
 
+  walk_namespaces (diagnose_inline_vars_for_namespace, /*data=*/0);
+
   lower_var_init ();
 
   generate_mangling_aliases ();
index 28b1c987153e696fa8012de09cf8f18c233d5d1e..028025deb7a0ded04223169dccf35f22b0200e26 100644 (file)
@@ -21923,7 +21923,8 @@ instantiate_decl (tree d, int defer_ok,
     {
       deleted_p = false;
       if (DECL_CLASS_SCOPE_P (code_pattern))
-       pattern_defined = ! DECL_IN_AGGR_P (code_pattern);
+       pattern_defined = (! DECL_IN_AGGR_P (code_pattern)
+                          || DECL_INLINE_VAR_P (code_pattern));
       else
        pattern_defined = ! DECL_EXTERNAL (code_pattern);
     }
index a063ea3439234bf5ad3c2a2c4605f5cdc9315875..121da32dd4b7b8e0c7deabc593ec5ed0bfdb7f91 100644 (file)
@@ -807,7 +807,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
       bool const_init;
       value = instantiate_non_dependent_expr (value);
       if (DECL_DECLARED_CONSTEXPR_P (decl)
-         || DECL_IN_AGGR_P (decl))
+         || (DECL_IN_AGGR_P (decl) && !DECL_VAR_DECLARED_INLINE_P (decl)))
        {
          /* Diagnose a non-constant initializer for constexpr.  */
          if (processing_template_decl
index 4376751a5297009e8a206b436ea27717f1f4c099..541faf75f1f508a0a29e55607a069114bedd3ccc 100644 (file)
@@ -22653,7 +22653,18 @@ gen_member_die (tree type, dw_die_ref context_die)
 
       child = lookup_decl_die (member);
       if (child)
-       splice_child_die (context_die, child);
+       {
+         /* Handle inline static data members, which only have in-class
+            declarations.  */
+         if (child->die_tag == DW_TAG_variable
+             && child->die_parent == comp_unit_die ())
+           {
+             reparent_child (child, context_die);
+             child->die_tag = DW_TAG_member;
+           }
+         else
+           splice_child_die (context_die, child);
+       }
 
       /* Do not generate standard DWARF for variant parts if we are generating
         the corresponding GNAT encodings: DIEs generated for both would
index a8c1f6163b2c579f3f3a38d3df9283ca2ccc26d3..5e31162e2c68e547c2f75d48351884f9b3041beb 100644 (file)
@@ -1,3 +1,13 @@
+2016-10-13  Jakub Jelinek  <jakub@redhat.com>
+
+       * g++.dg/cpp1z/inline-var1.C: New test.
+       * g++.dg/cpp1z/inline-var1a.C: New test.
+       * g++.dg/cpp1z/inline-var1.h: New file.
+       * g++.dg/cpp1z/inline-var2.C: New test.
+       * g++.dg/cpp1z/inline-var3.C: New test.
+       * g++.dg/concepts/decl-diagnose.C (struct X): Expect also error about
+       uninitialized const.
+
 2016-10-13  Sandra Loosemore <sandra@codesourcery.com>
 
        * gcc.target/arm/scd42-1.c: Skip if -mcpu incompatible with
index 9829ba1ba6977bab243e7449112b8f2dcecf539a..65785b25fe9b2856476974ea82fdbdf7c4e19ea5 100644 (file)
@@ -16,6 +16,7 @@ struct X
   template<typename T>
   static concept bool f6() { return true; } // { dg-error "a concept cannot be a member function" }
   static concept bool x; // { dg-error "declared 'concept'" }
+                        // { dg-error "uninitialized const" "" { target *-*-* } .-1 }
   concept int x2; // { dg-error "declared 'concept'" }
   concept ~X(); // { dg-error "a destructor cannot be 'concept'" }
   concept X(); // { dg-error "a constructor cannot be 'concept'" }
index 51612737847315d75797e5d2bcaaed1f067e4e56..381b8a4ea3224e6cd0d4c455cb0bc9e1b5dcf476 100644 (file)
@@ -6,3 +6,5 @@ struct A
   constexpr A() {}
   static constexpr A a[2] = {};  // { dg-error "22:elements of array 'constexpr const A A::a \\\[2\\\]' have incomplete type" }
 };
+
+// { dg-prune-output "storage size" }
index eeeae45cd73133dece122dd3000893d9caacc7be..c86dbe2c9111c34596c6a8a150ba37d9fbb8a607 100644 (file)
 #  error "__cpp_aligned_new != 201606"
 #endif
 
+#ifndef __cpp_inline_variables
+#  error "__cpp_inline_variables"
+#elif __cpp_inline_variables != 201606
+#  error "__cpp_inline_variables != 201606"
+#endif
+
 #ifndef __cpp_capture_star_this
 #  error "__cpp_capture_star_this"
 #elif __cpp_capture_star_this != 201603
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var1.C b/gcc/testsuite/g++.dg/cpp1z/inline-var1.C
new file mode 100644 (file)
index 0000000..c4fdaf8
--- /dev/null
@@ -0,0 +1,216 @@
+// { dg-do run }
+// { dg-options "-std=c++1z -Wno-deprecated" }
+// { dg-require-weak "" }
+// { dg-additional-sources "inline-var1a.C" }
+
+#include "inline-var1.h"
+
+static inline int var19 = bar (0);
+static int inline var20 = bar (1);
+extern inline int var23;
+inline int var21 = foo (6);
+inline int var22 = foo (7);
+extern inline int var23, var22;
+inline int var23 = foo (8);
+
+static int v, w;
+
+int
+foo (int x)
+{
+  if (x != v++)
+    __builtin_abort ();
+  return 36 + x;
+}
+
+int
+bar (int x)
+{
+  if (v < 6)
+    __builtin_abort ();
+  if ((x >> 4) != (w >> 4))
+    {
+      if ((x & 15) != 0 || (w & 15) != 2)
+       __builtin_abort ();
+      w = x + 1;
+    }
+  else if (x != w++)
+    __builtin_abort ();
+  return 46 + x;
+}
+
+int &ref1 = var1;
+int &ref2 = N::var2;
+const int &ref3 = S::var3;
+int &ref4 = S::var4;
+const int &ref5 = S::var5;
+const int &ref6 = N::var6;
+int &ref7 = var7;
+double &ref8 = N::var8;
+double &ref9 = S::var9;
+const int &ref11 = S::var11;
+int &ref12 = var12;
+int &ref13 = var13;
+int &ref14 = U::var14;
+T &ref15 = U::var15;
+T &ref16 = U::var16;
+int &ref17 = U::var17;
+const double &ref18 = U::var18;
+int &ref19 = var19;
+int &ref20 = var20;
+int &ref21 = var21;
+int &ref22 = var22;
+int &ref23 = var23;
+const int &ref24 = Y<int>::var24;
+int &ref25 = Y<int>::var25;
+int &ref26 = Y<int>::var26;
+int &ref27 = var27<int>;
+const int &ref28 = Y<int>::var28;
+const char &ref24a = Y<char>::var24;
+char &ref25a = Y<char>::var25;
+int &ref26a = Y<char>::var26;
+char &ref27a = var27<char>;
+const char &ref28a = Y<char>::var28;
+extern int &alt1;
+extern int &alt2;
+extern const int &alt3;
+extern int &alt4;
+extern const int &alt5;
+extern const int &alt6;
+extern int &alt7;
+extern double &alt8;
+extern double &alt9;
+extern const int &alt11;
+extern int &alt12;
+extern int &alt13;
+extern int &alt14;
+extern T &alt15;
+extern T &alt16;
+extern int &alt17;
+extern const double &alt18;
+extern int &alt19;
+extern int &alt20;
+extern int &alt21;
+extern int &alt22;
+extern int &alt23;
+extern const int &alt24;
+extern int &alt25;
+extern int &alt26;
+extern int &alt27;
+extern const int &alt28;
+extern const char &alt24a;
+extern char &alt25a;
+extern int &alt26a;
+extern char &alt27a;
+extern const char &alt28a;
+
+int
+main ()
+{
+  if (v != 9)
+    __builtin_abort ();
+  if (var1 != 4
+      || N::var2 != 0
+      || S::var3 != 5
+      || S::var4 != 6
+      || S::var5 != 7
+      || N::var6 != 8
+      || var7 != 9
+      || N::var8 != 2.0
+      || S::var9 != 3.0
+      || sizeof (N::var10) != 1
+      || S::var11 != 11
+      || var12 != 36
+      || var13 != 37
+      || U::var14 != 38
+      || U::var15.t != 39
+      || U::var16.t != 40
+      || U::var17 != 41
+      || U::var18 != 4.0
+      || var19 != 46
+      || var20 != 47
+      || var21 != 42
+      || var22 != 43
+      || var23 != 44
+      || Y<int>::var24 != 6
+      || Y<int>::var25 != 7
+      || Y<int>::var26 != 8
+      || var27<int> != 9
+      || Y<int>::var28 != 10
+      || Y<char>::var24 != 6
+      || Y<char>::var25 != 7
+      || Y<char>::var26 != 8
+      || var27<char> != 9
+      || Y<char>::var28 != 10)
+    __builtin_abort ();
+  if (ref1 != 4
+      || ref2 != 0
+      || ref3 != 5
+      || ref4 != 6
+      || ref5 != 7
+      || ref6 != 8
+      || ref7 != 9
+      || alt7 != 9
+      || ref8 != 2.0
+      || alt8 != 2.0
+      || ref9 != 3.0
+      || ref11 != 11
+      || ref12 != 36
+      || ref13 != 37
+      || ref14 != 38
+      || ref15.t != 39
+      || ref16.t != 40
+      || ref17 != 41
+      || ref18 != 4.0
+      || ref19 != 46
+      || alt19 != 62
+      || ref20 != 47
+      || alt20 != 63
+      || ref21 != 42
+      || ref22 != 43
+      || ref23 != 44
+      || ref24 != 6
+      || ref25 != 7
+      || ref26 != 8
+      || ref27 != 9
+      || ref28 != 10
+      || ref24a != 6
+      || ref25a != 7
+      || ref26a != 8
+      || ref27a != 9
+      || ref28a != 10)
+    __builtin_abort ();
+  if (&ref1 != &alt1
+      || &ref2 != &alt2
+      || &ref3 != &alt3
+      || &ref4 != &alt4
+      || &ref5 != &alt5
+      || &ref6 != &alt6
+      || &ref7 == &alt7
+      || &ref8 == &alt8
+      || &ref9 != &alt9
+      || &ref11 != &alt11
+      || &ref12 != &alt12
+      || &ref13 != &alt13
+      || &ref14 != &alt14
+      || &ref15 != &alt15
+      || &ref16 != &alt16
+      || &ref17 != &alt17
+      || &ref18 != &alt18
+      || &ref19 == &alt19
+      || &ref20 == &alt20
+      || &ref21 != &alt21
+      || &ref22 != &alt22
+      || &ref23 != &alt23
+      || &ref24 != &alt24
+      || &ref25 != &alt25
+      || &ref26 != &alt26
+      || &ref27 != &alt27
+      || &ref28 != &alt28
+      || &ref24a != &alt24a
+      || &ref25a != &alt25a
+      || &ref26a != &alt26a
+      || &ref27a != &alt27a
+      || &ref28a != &alt28a)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var1.h b/gcc/testsuite/g++.dg/cpp1z/inline-var1.h
new file mode 100644 (file)
index 0000000..675e71b
--- /dev/null
@@ -0,0 +1,46 @@
+inline int var1 = 4;
+static inline int var7 = 9;
+namespace N
+{
+  int inline var2;
+  inline const int var6 = 8;
+  static inline double var8 = 2.0;
+  extern inline char var10;
+}
+struct S
+{
+  static constexpr int var3 = 5;
+  static inline int var4 = 6;
+  static constexpr int var5 = 7;
+  static inline double var9 = 3.0;
+  static constexpr inline int var11 = 11;
+};
+const int S::var3;
+const int S::var3;
+extern int foo (int);
+extern int bar (int);
+struct T { T () { t = foo (3); } T (int x) { t = foo (x); } int t; };
+inline int var12 = foo (0);
+int inline var13 = foo (1);
+struct U
+{
+  static inline int var14 = foo (2);
+  static inline T var15;
+  static inline T var16 = 4;
+  static int inline var17 = foo (5);
+  static constexpr double var18 = 4.0;
+};
+template <typename T>
+struct Y
+{
+  static constexpr T var24 = 6;
+  static inline T var25 = 7;
+  static inline int var26 = 8;
+  static constexpr T var28 = 10;
+};
+template <typename T>
+const T Y<T>::var24;
+template <typename T>
+const T Y<T>::var24;
+template <typename T>
+inline T var27 = 9;
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var1a.C b/gcc/testsuite/g++.dg/cpp1z/inline-var1a.C
new file mode 100644 (file)
index 0000000..9b3da29
--- /dev/null
@@ -0,0 +1,44 @@
+// { dg-do compile }
+// { dg-options "-std=c++1z -Wno-deprecated -g" }
+
+#include "inline-var1.h"
+
+static inline int var19 = bar (16);
+static int inline var20 = bar (17);
+inline int var21 = foo (6);
+inline int var22 = foo (7);
+extern inline int var23;   
+inline int var23 = foo (8);
+
+int &alt1 = var1;
+int &alt2 = N::var2;   
+const int &alt3 = S::var3;
+int &alt4 = S::var4;   
+const int &alt5 = S::var5;
+const int &alt6 = N::var6;
+int &alt7 = var7;
+double &alt8 = N::var8;
+double &alt9 = S::var9; 
+const int &alt11 = S::var11;
+int &alt12 = var12;
+int &alt13 = var13;
+int &alt14 = U::var14;
+T &alt15 = U::var15;
+T &alt16 = U::var16;
+int &alt17 = U::var17;
+const double &alt18 = U::var18;
+int &alt19 = var19;
+int &alt20 = var20;
+int &alt21 = var21;
+int &alt22 = var22;
+int &alt23 = var23;
+const int &alt24 = Y<int>::var24;
+int &alt25 = Y<int>::var25;
+int &alt26 = Y<int>::var26;
+int &alt27 = var27<int>;
+const int &alt28 = Y<int>::var28;
+const char &alt24a = Y<char>::var24;
+char &alt25a = Y<char>::var25;
+int &alt26a = Y<char>::var26;
+char &alt27a = var27<char>;
+const char &alt28a = Y<char>::var28;
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var2.C b/gcc/testsuite/g++.dg/cpp1z/inline-var2.C
new file mode 100644 (file)
index 0000000..bfbbe1d
--- /dev/null
@@ -0,0 +1,117 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdeprecated" }
+
+inline int var1 = 4;                           // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+static inline int var7 = 9;                    // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+namespace N
+{
+  int inline var2;                             // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  inline const int var6 = 8;                   // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline double var8 = 2.0;             // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  extern inline char var10;                    // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+}
+struct S
+{
+  static constexpr int var3 = 5;
+  static inline int var4 = 6;                  // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static constexpr int var5 = 7;
+  static inline double var9 = 3.0;             // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static constexpr inline int var11 = 11;      // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+};
+const int S::var3;                             // { dg-warning "redundant redeclaration of" "" { target c++1z } }
+const int S::var3;                             // { dg-error "redefinition of" "" { target c++14_down } }
+extern int foo (int);                          // { dg-warning "redundant redeclaration of" "" { target c++1z } .-1 }
+extern int bar (int);
+struct T { T () { t = foo (3); } T (int x) { t = foo (x); } int t; };
+inline int var12 = foo (0);                    // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+int inline var13 = foo (1);                    // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+struct U
+{
+  static inline int var14 = foo (2);           // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline T var15;                       // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline T var16 = 4;                   // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static int inline var17 = foo (5);           // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static constexpr double var18 = 4.0;
+};
+extern inline int var19;                       // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+extern inline int var20;                       // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+int &ref19 = var19;                            // { dg-error "odr-used inline variable 'var19' is not defined" "" { target *-*-* } .-2 }
+int sz20 = sizeof (var20);
+struct V
+{
+  static struct A var21;                       // { dg-warning "inline variables are only available with" "" { target c++14_down } .+1 }
+  static inline struct B var22;                        // { dg-error "has incomplete type" }
+  static inline struct C var23 = {};           // { dg-error "has incomplete type" }
+};                                             // { dg-warning "inline variables are only available with" "" { target c++14_down } .-1 }
+struct W
+{
+  static inline int var24;                     // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline const int var25;               // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+                                               // { dg-error "uninitialized const" "" { target *-*-* } .-1 }
+  static inline int var26 = 5;                 // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline const int var27 = 6;           // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline double var28 = { 4.0 };                // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static const inline double var29 = { 5.0 };  // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+};
+int W::var24;                                  // { dg-error "redefinition of" }
+const int W::var25;                            // { dg-error "redefinition of" }
+int W::var26;                                  // { dg-error "redefinition of" }
+const int W::var27;                            // { dg-error "redefinition of" }
+double W::var28;                               // { dg-error "redefinition of" }
+double const W::var29;                         // { dg-error "redefinition of" }
+struct X
+{
+  inline int var30;                            // { dg-error "'var30' declared as an 'inline' field" }
+};
+inline typedef int TT;                         // { dg-error "'TT' declared as an 'inline' type" }
+int
+foo (inline int var31)                         // { dg-error "'var31' declared as an 'inline' parameter" }
+{
+  inline int var32;                            // { dg-error "'inline' specifier invalid for variable 'var32' declared at block scope" }
+  static inline int var33;                     // { dg-error "'inline' specifier invalid for variable 'var33' declared at block scope" }
+}
+template <typename A, typename B, typename C>
+struct Y
+{
+  static A var34;                              // { dg-warning "inline variables are only available with" "" { target c++14_down } .+1 }
+  static inline B var35;                       // { dg-error "has incomplete type" }
+  static inline C var36;                       // { dg-error "has incomplete type" }
+};                                             // { dg-warning "inline variables are only available with" "" { target c++14_down } .-1 }
+struct A;
+struct B;
+struct C;
+Y<A, B, C> y;
+A *ptr34 = &Y<A, B, C>::var34;
+B *ptr35 = &Y<A, B, C>::var35;
+C *ptr36 = &Y<A, B, C>::var36;
+template <int N>
+struct Z
+{
+  static inline int var37;                     // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline const int var38;               // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+                                               // { dg-error "uninitialized const" "" { target *-*-* } .-1 }
+  static inline int var39 = 5;                 // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline const int var40 = 6;           // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static inline double var41 = { 4.0 };                // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static const inline double var42 = { 5.0 };  // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  static constexpr int var43 = 5;
+  static constexpr inline int var44 = 5;       // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+};
+template <int N>
+int Z<N>::var37;                               // { dg-error "redefinition of" }
+template <int N>
+const int Z<N>::var38;                         // { dg-error "redefinition of" }
+const int &ref38 = Z<0>::var38;
+template <int N>
+int Z<N>::var39;                               // { dg-error "redefinition of" }
+template <int N>
+const int Z<N>::var40;                         // { dg-error "redefinition of" }
+template <int N>
+double Z<N>::var41;                            // { dg-error "redefinition of" }
+template <int N>
+double const Z<N>::var42;                      // { dg-error "redefinition of" }
+template <int N>
+const int Z<N>::var43;                         // { dg-warning "redundant redeclaration of" "" { target c++1z } }
+template <int N>                               // { dg-warning "redundant redeclaration of" "" { target c++1z } .+1 }
+const int Z<N>::var43;                         // { dg-error "redefinition of" "" { target c++14_down } }
+Z<0> z;
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var3.C b/gcc/testsuite/g++.dg/cpp1z/inline-var3.C
new file mode 100644 (file)
index 0000000..7bee9dc
--- /dev/null
@@ -0,0 +1,58 @@
+// { dg-do compile }
+// { dg-options "-g0" }
+// Verify that inline variables and static data members that aren't odr-used
+// aren't emitted into assembly even at -O0.
+// { dg-final { scan-assembler-not "inlvarvariable" } }
+
+inline int inlvarvariable1 = 1;                                // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+const inline int inlvarvariable2 = 2;                  // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+namespace N
+{
+  int inline inlvarvariable3;                          // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  const int inline inlvarvariable4 = 4;                        // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+}
+struct S
+{
+  static inline double inlvarvariable5 = 5.0;          // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+#if __cplusplus >= 201103L
+  static constexpr int inlvarvariable6 = 6;
+  static inline constexpr int inlvarvariable7 = 7;     // { dg-warning "inline variables are only available with" "" { target { c++11 && c++14_down } } }
+#endif
+};
+template <int N>                                       // { dg-warning "variable templates only available with" "" { target c++11_down } .+1 }
+inline int inlvarvariable8;                            // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+template <int N>                                       // { dg-warning "variable templates only available with" "" { target c++11_down } .+1 }
+const int inline inlvarvariable9 = 9;                  // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+namespace N
+{
+  template <int N>                                     // { dg-warning "variable templates only available with" "" { target c++11_down } .+1 }
+  int inline inlvarvariable10 = 10;                    // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+  template <int N>                                     // { dg-warning "variable templates only available with" "" { target c++11_down } .+1 }
+  const inline double inlvarvariable11 = 11.0;         // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+}
+template <int N>
+struct T
+{
+  static inline int inlvarvariable12 = 12;             // { dg-warning "inline variables are only available with" "" { target c++14_down } }
+#if __cplusplus >= 201103L
+  static constexpr int inlvarvariable13 = 13;
+  static inline constexpr double inlvarvariable14 = 14.0; // { dg-warning "inline variables are only available with" "" { target { c++11 && c++14_down } } }
+#endif
+};
+#if __cplusplus < 201103L
+#define decltype(x) int
+#endif
+decltype (inlvarvariable1) v1 = inlvarvariable2 + sizeof (inlvarvariable1);
+decltype (N::inlvarvariable3) v2 = N::inlvarvariable4 + sizeof (N::inlvarvariable3);
+#if __cplusplus >= 201103L
+decltype (S::inlvarvariable6) v3 = sizeof (S::inlvarvariable5) + S::inlvarvariable6 + S::inlvarvariable7;
+#else
+int v3 = sizeof (S::inlvarvariable5);
+#endif
+decltype (inlvarvariable8<2>) v4 = inlvarvariable9<2> + sizeof (inlvarvariable8<2>);
+decltype (N::inlvarvariable10<0>) v5 = sizeof (N::inlvarvariable10<0>) + sizeof (N::inlvarvariable11<0>);
+#if __cplusplus >= 201103L
+decltype (T<-1>::inlvarvariable12) v6 = sizeof (T<-1>::inlvarvariable14) + sizeof (T<-1>::inlvarvariable12) + T<-1>::inlvarvariable13;
+#else
+int v6 = sizeof (T<-1>::inlvarvariable12);
+#endif
index 93b241b9e0403bed0da20c2944ba6a2aafb355b3..c7d35b7f71e96f4fa8af65908c9cd6aa29108304 100644 (file)
@@ -1,7 +1,7 @@
 // { dg-do assemble  }
 // GROUPS passed miscellaneous
 // test that use of `inline' is forbidden when it should be
-inline int i;// { dg-error "" } .*
+inline int i;// { dg-error "" "" { target c++14_down } } .*
 struct c { inline int i; };// { dg-error "" } .*
 int foo (inline int i);// { dg-error "" } .*
 inline class c; // { dg-error "'inline' can only be specified for functions" } inline