// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y -triple x86_64-linux-gnu %s

// If there is a preceding declaration of the entity *in the same scope* in
// which the bound was specified, an omitted array bound is taken to be the
// same as in that earlier declaration

// rdar://13535367
namespace test0 {
  extern "C" int array[];
  void declare() { extern int array[100]; }
  int value1 = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
  extern "C" int array[];
  int value2 = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}

namespace test1 {
  extern "C" int array[];
  void test() {
    { extern int array[100]; }
    extern int array[];
    int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
  }
}

namespace test2 {
  void declare() { extern int array[100]; }
  extern int array[];
  int value = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}

namespace test3 {
  void test() {
    { extern int array[100]; }
    extern int array[];
    int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
  }
}

namespace test4 {
  extern int array[];
  void test() {
    extern int array[100];
    int x = sizeof(array);
  }
  int y = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}

namespace test5 {
  void test() {
    extern int array[100];
    extern int array[];
    int x = sizeof(array);
  }
}

namespace test6 {
  void test() {
    extern int array[100];
    {
      extern int array[];
      int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
    }
    int y = sizeof(array);
    extern int array[];
    int z = sizeof(array);
  }
}

namespace test7 {
  extern int array[100];
  void test() {
    extern int array[];
    int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
  }
  int y = sizeof(array);
  extern int array[];
  int z = sizeof(array);
}

namespace test8 {
  extern int array[];
  void test() {
    extern int array[100];
    int x = sizeof(array);
  }
  int y = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
  extern int array[];
  int z = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}

namespace dependent {
  template<typename T> void f() {
    extern int arr1[];
    extern T arr1;
    extern T arr2;
    extern int arr2[];
    static_assert(sizeof(arr1) == 12, "");
    static_assert(sizeof(arr2) == 12, "");

    // Use a failing test to ensure the type isn't considered dependent.
    static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}}
  }

  void g() { f<int[3]>(); } // expected-note {{in instantiation of}}

  template<typename T> void h1() {
    extern T arr3;
    {
      int arr3;
      {
        extern int arr3[];
        // Detected in template definition.
        (void)sizeof(arr3); // expected-error {{incomplete}}
      }
    }
  }

  template<typename T> void h2() {
    extern int arr4[3];
    {
      int arr4;
      {
        extern T arr4;
        // Detected in template instantiation.
        (void)sizeof(arr4); // expected-error {{incomplete}}
      }
    }
  }

  void i() {
    h1<int[3]>();
    h2<int[]>(); // expected-note {{in instantiation of}}
  }

  int arr5[3];
  template<typename T> void j() {
    extern T arr5;
    extern T arr6;
    (void)sizeof(arr5); // expected-error {{incomplete}}
    (void)sizeof(arr6); // expected-error {{incomplete}}
  }
  int arr6[3];

  void k() { j<int[]>(); } // expected-note {{in instantiation of}}

  template<typename T, typename U> void l() {
    extern T arrX; // expected-note {{previous}}
    extern U arrX; // expected-error {{different type: 'int [4]' vs 'int [3]'}}
    (void)sizeof(arrX); // expected-error {{incomplete}}
  }

  void m() {
    l<int[], int[3]>(); // ok
    l<int[3], int[]>(); // ok
    l<int[3], int[3]>(); // ok
    l<int[3], int[4]>(); // expected-note {{in instantiation of}}
    l<int[], int[]>(); // expected-note {{in instantiation of}}
  }

  template<typename T> void n() {
    extern T n_var; // expected-error {{redeclaration of 'n_var' with a different type: 'double' vs 'int'}} expected-note {{previous}}
    extern T n_fn(); // expected-error {{functions that differ only in their return type cannot be overloaded}} expected-note {{previous}}
  }
  template void n<int>();
  template void n<double>(); // expected-note {{in instantiation of}}

  template<typename T> void o() {
    extern T o_var; // expected-note {{previous}}
    extern T o_fn(); // expected-note {{previous}}
  }
  template void o<int>();
  float o_var; // expected-error {{redefinition of 'o_var' with a different type: 'float' vs 'int'}}
  float o_fn(); // expected-error {{functions that differ only in their return type cannot be overloaded}}

  int p_var;
  int p_fn();
  template<typename T> void p() {
    extern T p_var;
    extern T p_fn();
  }
}

namespace use_outside_ns {
  namespace A {
    extern int a[3];
    extern int b[];
    extern int c[3];
    void f() {
      extern int a[];
      extern int b[3];
    }
    template<typename T> void x() {
      extern T c;
      extern T d;
    }
    extern int d[3];
    template void x<int[]>();
  }
  int w = sizeof(A::a);
  int x = sizeof(A::b); // expected-error {{incomplete}}
  int y = sizeof(A::c);
  int z = sizeof(A::d);
  namespace A {
    int g() { return sizeof(a); }
    int h() { return sizeof(b); } // expected-error {{incomplete}}
    int i() { return sizeof(c); }
    int j() { return sizeof(d); }
  }
}
