Bug 48948 - [C++0x] constexpr friend function cannot be defined in-class
Summary: [C++0x] constexpr friend function cannot be defined in-class
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.7.0
: P3 normal
Target Milestone: 4.7.0
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-05-10 12:22 UTC by Daniel Krügler
Modified: 2011-05-20 19:02 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Krügler 2011-05-10 12:22:53 UTC
gcc 4.7.0 20110507 (experimental) in C++0x mode rejects the following code at the line marked with #:

//---
struct B {
  friend constexpr int f(B) { return 0; } // #
};
//---

"error: invalid type for parameter 1 of constexpr function 'constexpr int f(B)'"

This code should be accepted. 

The same problem occurs with friend operators, like this case

      friend constexpr int operator+(B) { return 0; }

within B resulting in:

"error: invalid type for parameter 1 of constexpr function 'constexpr int operator+(B)'"

It seems to me that this defect is *not* a dup of bug 48945, where the compiler incorrectly attempts to add a const-qualifier to a static member function. I can only assume that the compiler considers the argument as incomplete and thus not being a valid literal type, because other non-friend constexpr functions don't cause such problems, but I'm not sure.

Finally, the problem does not occur, if the friend function is defined out-of-class, like so:

    class C {
      friend constexpr int g(C);
    };

    constexpr int g(C) { return 0; }
Comment 1 Johannes Schaub 2011-05-10 14:49:18 UTC
(In reply to comment #0)
> gcc 4.7.0 20110507 (experimental) in C++0x mode rejects the following code at
> the line marked with #:
> 
> //---
> struct B {
>   friend constexpr int f(B) { return 0; } // #
> };
> //---
> 
> "error: invalid type for parameter 1 of constexpr function 'constexpr int
> f(B)'"
> 
> This code should be accepted. 
> 
> The same problem occurs with friend operators, like this case
> 
>       friend constexpr int operator+(B) { return 0; }
> 
> within B resulting in:
> 
> "error: invalid type for parameter 1 of constexpr function 'constexpr int
> operator+(B)'"
> 

Well, class "B" is incomplete in the parameter type list of f. The spec allows B to be incomplete for that parameter type (8.3.5p9), but it doesn't say anything what that means for determining whether or not the parameter has literal class type (several of the bullets of 3.9p10 require a complete class type to be checked). I don't know whether an implementation is supposed to reject the code, or supposed to wait until B is complete. Same would apply for non-static member functions.

Any hints?
Comment 2 Daniel Krügler 2011-05-10 16:33:39 UTC
(In reply to comment #1)

I don't think that this is intended, but I would like to await feedback from the developer group before submitting a corresponding core issue. IMO there are some core wordings in regard to type completeness that are not intended to be read strictly, e.g. 3.2 p.4

"— a function with a return type or argument type of type T is defined (3.1) [..]"

can be read to disallow any of the following function definitions:

struct A {
  A f(A a) { return a; }
  friend A g(A a) { return a; }
};

I haven't seen a compiler who rejects this code, because they all contribute more weight to 9.2 p.2. In regard to constexpr functions the clear intention is that these can be forward-declared and are only required to be completely defined, if used in a manner that requires a constant expression. The example in 7.1.5 with the constexpr function "small" and it's usage in the constexpr constructor of pixel make this intention very clear. Now to me, the 3.2 p.4 requirement was always supposed to be interpreted as "complete as it would be required to be complete within the function body" for both function parameters and function return types within this function definition, so I don't see any reason why the compiler needs to validate the constraints on literal types at the point where the class type is still incomplete and *does* not need to be complete.

If the gcc developer feedback indeed points to an intended conservative reading of 3.2 p.4 for constexpr functions as in this example I will surely require to open a core issue for this situation, because this severely constraints constexpr function definitions within classes. In this case a simple workaround is to replace the "by-value" parameter by "by-reference", but the need for such a change looks like an artifact to me. It seems to me that both functions f() and g() defined in class A above could be constexpr functions.
Comment 3 Johannes Schaub 2011-05-10 16:46:18 UTC
(In reply to comment #2)
> (In reply to comment #1)
> 
> I don't think that this is intended, but I would like to await feedback from
> the developer group before submitting a corresponding core issue. IMO there are
> some core wordings in regard to type completeness that are not intended to be
> read strictly, e.g. 3.2 p.4
> 
> "— a function with a return type or argument type of type T is defined (3.1)
> [..]"
> 
> can be read to disallow any of the following function definitions:
> 
> struct A {
>   A f(A a) { return a; }
>   friend A g(A a) { return a; }
> };
> 
> I haven't seen a compiler who rejects this code, because they all contribute
> more weight to 9.2 p.2. 

Within "A a" (the parameters) and within "A f" / "A g" (the return types), the class A is incomplete. Only within the function bodies, the class A is complete by 9.2p2. 

The compilers accept that code because of 8.3.5p9. 3.2 p4 is a non-normative big note, which can impossibly achieve the precision of covering every detail. It only provides a rough description.
Comment 4 Johannes Schaub 2011-05-10 16:59:50 UTC
(In reply to comment #0)
> gcc 4.7.0 20110507 (experimental) in C++0x mode rejects the following code at
> the line marked with #:
> 
> //---
> struct B {
>   friend constexpr int f(B) { return 0; } // #
> };
> //---
> 
> "error: invalid type for parameter 1 of constexpr function 'constexpr int
> f(B)'"
> 
> This code should be accepted. 
> 

I remember that Gaby (I hope I remember correctly, hehe) argued that the body of "f" is late-parsed. Hence, the argument, until after the end of definition of "B", "f" is only considered declared but not defined.

I don't share that view though. That view is an implementation detail. In the language, the function marked by "#" is defined as soon as its body has been completely seen. If something else is intended, the specification needs to be explicit about this. Unless I've missed the text, it is not explicit about that.
Comment 5 Johannes Schaub 2011-05-10 17:07:30 UTC
(In reply to comment #4)
> (In reply to comment #0)
> > gcc 4.7.0 20110507 (experimental) in C++0x mode rejects the following code at
> > the line marked with #:
> > 
> > //---
> > struct B {
> >   friend constexpr int f(B) { return 0; } // #
> > };
> > //---
> > 
> > "error: invalid type for parameter 1 of constexpr function 'constexpr int
> > f(B)'"
> > 
> > This code should be accepted. 
> > 
> 
> I remember that Gaby (I hope I remember correctly, hehe) argued that the body
> of "f" is late-parsed. Hence, the argument, until after the end of definition
> of "B", "f" is only considered declared but not defined.
> 

Consider also this, which I think demonstrates that position:

struct A {
  struct B;
  void f(B) { }
  struct B { };
};

I think this is ill-formed by the spec, because "B" is incomplete in f's parameter and f is defined at that point. And this is not one of the contexts that B is allowed to be incomplete by 8.3.5p9.

However, clang, gcc and comeau online all accept this. Apparently, they consider "f" only declared and not defined.

I would be glad if someone points me to the rule that says that "f" is only considered defined at the closing "}" of A though.
Comment 6 Johannes Schaub 2011-05-10 17:20:31 UTC
(In reply to comment #4)
> (In reply to comment #0)
> > gcc 4.7.0 20110507 (experimental) in C++0x mode rejects the following code at
> > the line marked with #:
> > 
> > //---
> > struct B {
> >   friend constexpr int f(B) { return 0; } // #
> > };
> > //---
> > 
> > "error: invalid type for parameter 1 of constexpr function 'constexpr int
> > f(B)'"
> > 
> > This code should be accepted. 
> > 
> 
> I remember that Gaby (I hope I remember correctly, hehe) argued that the body
> of "f" is late-parsed. Hence, the argument, until after the end of definition
> of "B", "f" is only considered declared but not defined.
> 
> I don't share that view though. That view is an implementation detail. In the
> language, the function marked by "#" is defined as soon as its body has been
> completely seen. If something else is intended, the specification needs to be
> explicit about this. Unless I've missed the text, it is not explicit about
> that.

But I can live with that. Having remembered the intent as described, I see that the friend function you already showed is valid, because when the body is late parsed, and the function is then considered defined only when the enclosing class is complete, it makes sense. I wouldn't intuitively get to this result though, but I think it's an interpretation I can live with!

I'm sorry for that load of nonsense in the comment boxes. I promise this is the last comment in this row of this PR! :)
Comment 7 Jason Merrill 2011-05-11 21:30:23 UTC
Author: jason
Date: Wed May 11 21:30:18 2011
New Revision: 173683

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=173683
Log:
	PR c++/48948
	* semantics.c (validate_constexpr_fundecl): Defer checking if
	an argument type is being defined.
	(is_valid_constexpr_fn): Add defer_ok parm.
	(cxx_eval_call_expression): Adjust.
	(check_deferred_constexpr_decls): New.
	(literal_type_p): Make sure type isn't being defined.
	(ensure_literal_type_for_constexpr_object): Handle type being defined.
	* cp-tree.h: Declare check_deferred_constexpr_decls.
	* decl.c (grokfndecl): Call validate_constexpr_fundecl here.
	(start_preparsed_function, cp_finish_decl): Not here.
	* class.c (finalize_literal_type_property): Don't call
	validate_constexpr_fundecl.
	(finish_struct): Call check_deferred_constexpr_decls.
	* pt.c (tsubst_decl): Call validate_constexpr_fundecl.
	(instantiate_class_template): Call check_deferred_constexpr_decls.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/class.c
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/cp/decl.c
    trunk/gcc/cp/pt.c
    trunk/gcc/cp/semantics.c
    trunk/gcc/testsuite/ChangeLog
Comment 8 Jason Merrill 2011-05-12 01:40:30 UTC
Fixed.
Comment 9 Jason Merrill 2011-05-18 17:19:19 UTC
Author: jason
Date: Wed May 18 17:19:15 2011
New Revision: 173869

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=173869
Log:
	PR c++/48948
	PR c++/49015
	* class.c (finalize_literal_type_property): Do check
	for constexpr member functions of non-literal class.
	(finish_struct): Don't call check_deferred_constexpr_decls.
	* cp-tree.h: Don't declare it.
	(DECL_DEFERRED_CONSTEXPR_CHECK): Remove.
	* decl.c (grok_special_member_properties): Don't check it
	(grokfnedcl): Don't call validate_constexpr_fundecl.
	(start_preparsed_function): Do call it.
	* pt.c (tsubst_decl): Don't call it.
	(instantiate_class_template_1): Don't call
	check_deferred_constexpr_decls.
	* semantics.c (literal_type_p): Check for any incompleteness.
	(ensure_literal_type_for_constexpr_object): Likewise.
	(is_valid_constexpr_fn): Revert deferral changes.
	(validate_constexpr_fundecl): Likewise.
	(register_constexpr_fundef): Likewise.
	(check_deferred_constexpr_decls): Remove.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete3.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/class.c
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/cp/decl.c
    trunk/gcc/cp/pt.c
    trunk/gcc/cp/semantics.c
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
Comment 10 Jason Merrill 2011-05-20 19:02:46 UTC
Author: jason
Date: Fri May 20 19:02:42 2011
New Revision: 173975

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=173975
Log:
	PR c++/48948
	* class.c (finalize_literal_type_property): Only check
	for constexpr member functions of non-literal class.
	* decl.c (cp_finish_decl): Don't call validate_constexpr_fundecl.
	* semantics.c (literal_type_p): Call complete_type.

Added:
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete3.C
Modified:
    branches/gcc-4_6-branch/gcc/cp/ChangeLog
    branches/gcc-4_6-branch/gcc/cp/class.c
    branches/gcc-4_6-branch/gcc/cp/decl.c
    branches/gcc-4_6-branch/gcc/cp/semantics.c
    branches/gcc-4_6-branch/gcc/testsuite/ChangeLog
    branches/gcc-4_6-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C