Bug 56208 - [4.8 Regression] Some classic sfinae cases fail to work due to access problems
Summary: [4.8 Regression] Some classic sfinae cases fail to work due to access problems
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.8.0
: P3 normal
Target Milestone: 4.8.0
Assignee: Jason Merrill
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2013-02-04 19:37 UTC by Daniel Krügler
Modified: 2013-02-06 03:51 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 4.7.2
Known to fail:
Last reconfirmed: 2013-02-04 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Krügler 2013-02-04 19:37:43 UTC
The following program becomes rejected when compiled with gcc 4.8.0 trunk using the flags:

-Wall -pedantic

(with or without -std=c++11)

//--------------------------------
struct ostream {
  ostream& operator<<(int);
};

struct sfinae_base {

  typedef char one;
  typedef char (&two)[2];

  template<class T>
  static T make();

  template<unsigned> struct ok { typedef int type; };

  template<class U, class T>
  static one test(typename ok<sizeof(
    make<U>() << make<T>()
  )>::type);

  template<class, class>
  static two test(...);

};

template<class T>
struct is_printable : private sfinae_base
{
  enum { value = sizeof(test<ostream&, T>(0)) == sizeof(one) };
};

typedef int ok[is_printable<int>::value ? 1 : -1];

int main() {}
//--------------------------------

"Compilation finished with errors:
source.cpp:31:49: error: size of array 'ok' is negative
typedef int ok[is_printable<int>::value ? 1 : -1];
^"

It worked with gcc 4.7.2 (also with Clang 3.2 or Intel-13), so this looks like a regression to me. Operator<< is not the only one, I also noted problems with other operators (such as binary plus).
Comment 1 Daniel Krügler 2013-02-04 19:54:47 UTC
I just notice that the problem is not restricted to sizeof sfinae. In fact if we define the first test overload as follows:

template<class U, class T>
  static one test(decltype(
    (make<U>() << make<T>()), 0
  ));

the same regression problem occurs. I'm confused.
Comment 2 Daniel Krügler 2013-02-04 19:57:18 UTC
The actually tested gcc version was 4.8.0 20130127 (experimental)
Comment 3 Paolo Carlini 2013-02-04 20:23:52 UTC
Let's mark it as such then.
Comment 4 Daniel Krügler 2013-02-04 21:10:19 UTC
Here are two further variants of the first overload that fail to work since 4.8.0 trunk:

(a)
  template<class> struct res { typedef one type; };

  template<class U, class T>
  static typename res<decltype(make<U>() << make<T>())>::type
  test(int);

(b)
  template<class> struct res { typedef one type; };

  template<class U, class T>
  static one
  test(typename res<decltype(make<U>() << make<T>())>::type*);

Obviously the actual problem is not related to sizeof, so I changed the issue title accordingly.
Comment 5 Daniel Krügler 2013-02-05 06:37:06 UTC
I think I found the problem, the root is actually not related to sfinae (fortunately), but to the way how name-lookup in classes work in gcc. The problem can be fixed (as a workaround), if we move the static member function

template<class T>
static T make();

into namespace scope (as non-member function). I apologize for the lengthy thread within this issue. But it nonetheless is a regression, because that name-lookup worked correctly in previous versions of gcc.
Comment 6 Paolo Carlini 2013-02-05 09:50:11 UTC
Thanks Daniel. Next, we have to figure out which commit broke such lookups. I can work on that today (if nobody beats me)
Comment 7 Paolo Carlini 2013-02-05 11:25:46 UTC
Narrowed to r190095 - r190842 so far.
Comment 9 Daniel Krügler 2013-02-05 19:09:15 UTC
Further data about the root of the problem: It seems actually to be an access problem, the requirements for reproducing seem to be:

1) Some class B derives *privately* from a base class A

2) B refers to some function template f2 that refers to another dependent function template f1 both in class scope of A (It doesn't matter whether these function are actually public in A)

A reduced example is as follows:

//---------------------------
struct A {
   template<class T>
   static int f1();
   
   template<class T>
   static int f2(char(*)[sizeof(f1<T>())]);
};

struct B : private A {
   enum { value = sizeof(f2<int>(0)) };
};
//---------------------------

"Compilation finished with errors:
source.cpp:10:35: error: no matching function for call to 'B::f2(int)'
enum { value = sizeof(f2<int>(0)) };
^
source.cpp:10:35: note: candidate is:
source.cpp:6:15: note: template<class T> static int A::f2(char (*)[sizeof (f1<T>())])
static int f2(char(*)[sizeof(f1<T>())]);
^
source.cpp:6:15: note: template argument deduction/substitution failed:
source.cpp: In substitution of 'template<class T> static int A::f2(char (*)[sizeof (f1<T>())]) [with T = int]':
source.cpp:10:35: required from here
source.cpp:3:15: error: 'static int A::f1() [with T = int]' is inaccessible
static int f1();
^
source.cpp:6:39: error: within this context
static int f2(char(*)[sizeof(f1<T>())]);
^"
Comment 10 Jason Merrill 2013-02-06 03:33:52 UTC
Author: jason
Date: Wed Feb  6 03:33:45 2013
New Revision: 195779

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=195779
Log:
	PR c++/56208
	* pt.c (fn_type_unification): Discard any access checks from
	substituting explicit args.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/sfinae43.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/pt.c
Comment 11 Jason Merrill 2013-02-06 03:51:00 UTC
Fixed.