Š¢hanks, could you (or smb else with commit perms) check it in?
--Vassil
On 18/02/16 03:24, Richard Smith wrote:
(And otherwise this LGTM.)
On Wed, Feb 17, 2016 at 5:24 PM, Richard Smith <rich...@metafoo.co.uk> wrote:
+ // TODO: Check visibility. New is hidden but has a complete type. If New
+ // has no array bound, it should not inherit one from Old, if Old is not
+ // visible.
I think this is in the wrong place: it should be attached to the "old
is complete and new is not" case. Also, our convention is to use
FIXME, not TODO.
Speaking of which, you can now delete the "FIXME: Even if this merging
succeeds, [...]" comment now, and add its example as a testcase. With
your change we should reliably diagnose it.
On Wed, Feb 17, 2016 at 2:32 PM, Vassil Vassilev <v.g.vassi...@gmail.com> wrote:
On 16/02/16 22:20, Richard Smith wrote:
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -419,6 +419,25 @@ static bool isPreferredLookupResult(Sema &S,
Sema::LookupNameKind Kind,
}
}
+ // VarDecl can have incomplete array types, prefer the one with more
complete
+ // array type.
+ if (VarDecl *DVD = dyn_cast<VarDecl>(DUnderlying)) {
+ VarDecl *EVD = cast<VarDecl>(EUnderlying);
+ // Skip local VarDecls with incomplete array type.
+ if ((!DVD->isLocalVarDecl() && DVD->hasExternalStorage()) ||
+ (!EVD->isLocalVarDecl() && EVD->hasExternalStorage())) {
Why do you need this special case?
No reason, I decided to be conservative. All comments should be addressed in
the new version of the patch.
+
+ ASTContext &C = S.getASTContext();
+
+ if (const ArrayType *DVDTy = C.getAsArrayType(DVD->getType())) {
+ const ArrayType *EVDTy = C.getAsArrayType(EVD->getType());
+ // Prefer the decl with more complete type if visible.
+ return !DVDTy->isIncompleteArrayType() &&
EVDTy->isIncompleteArrayType()
Checking for an array type here seems unnecessary -- it'd be simpler to
check EVD->getType()->isIncompleteType() &&
!DVD->getType()->isIncompleteType().
+ && S.isVisible(D);
This seems like a very subtle case: we're performing redeclaration lookup
for a variable declaration X, and we find two results D and E. D is hidden
but has a complete type. E is visible but has an incomplete type. If X has
no array bound, it should not inherit one from D. But if it does have an
array bound, and that bound differs from E's bound, we should diagnose the
mismatch.
Please add another test to the end of merge-incomplete-array-vars.cpp to
check this is working:
int c[2]; // expected-error {{different type: 'int [2]' vs 'int [1]'}}
That said, I think this test will fail if you reorder c1 and c2 in the
module map, but that's a pre-existing bug (see the last FIXME in
Sema::MergeVarDeclTypes).
+ }
+ }
+ }
On Tue, Feb 16, 2016 at 11:12 AM, Vassil Vassilev <v.g.vassi...@gmail.com>
wrote:
ping...
On 07/02/16 22:33, Vassil Vassilev wrote:
Improve a comment.
--Vassil
On 07/02/16 20:48, Vassil Vassilev wrote:
Would this patch be any better?
--Vassil
On 05/02/16 21:49, Richard Smith wrote:
This belongs in ASTDeclReader::attachPreviousDecl[Impl]. That's where we
propagate data that's supposed to be consistent across a redeclaration chain
from earlier declarations to later ones.
There's another wrinkle here: we should only be propagating the type from
a previous declaration that's declared in the same scope (in particular, we
should skip over declarations declared as local extern declarations within a
function). This may in some cases require walking backwards along the
redeclaration chain to find the previous declaration that was not a local
extern declaration. That'd be observable in a case like this:
modulemap:
module A { module A1 { header "a1.h" } module A2 { header "a2.h" } }
module B { header "b.h" }
a1.h:
int a[];
b.h:
void g(int(*)[], int);
void g(int(*)[1], int) = delete;
template<typename T> void f(T t) {
extern int a[];
g(&a, t);
}
a2.h:
int a[1];
x.cc:
#include "a1.h"
#include "b.h"
void h() { f(0); } // should not produce an error; type of 'a' within 'f'
should not have been updated
That example actually reveals another problem: we really don't want the
completed type to be visible unless there is a visible declaration with the
completed type. That suggests that propagating the type across the
redeclaration chain may be the wrong approach, and we should instead handle
this by changing isPreferredLookupResult (in SemaLookup.cpp) to prefer a
VarDecl with a complete type over one with an incomplete type.
On Fri, Feb 5, 2016 at 12:27 PM, Vassil Vassilev <v.g.vassi...@gmail.com>
wrote:
I am not sure where else to put this logic if not in isSameEntity...
Could you point me to a better place?
--Vassil
On 05/02/16 19:56, Richard Smith wrote:
On Fri, Feb 5, 2016 at 7:04 AM, Vassil Vassilev <v.g.vassi...@gmail.com>
wrote:
Good point. Do you have in mind calling 'clang::Sema::MergeVarDeclTypes'
or we to just "duplicate" the logic in
clang::ASTDeclReader::mergeRedeclarable?
It's not safe to call into Sema while we're in a partially-deserialized
state. We know the only interesting case here is complete versus incomplete
array types, so we don't need anything like the complexity of
MergeVarDeclTypes -- something as easy as "if (new type is incomplete and
old type is not) set new type to old type" should work.
On 05/02/16 02:50, Richard Smith via cfe-commits wrote:
I suspect we'll need to do a little more than this: when we rebuild the
redeclaration chain, we should probably propagate the complete type to later
redeclarations in the same scope. Otherwise, if we import a module that has
a complete type and one that has an incomplete type, the declaration found
by name lookup might be the one with the incomplete type, possibly resulting
in rejects-valid on code like this:
a.h:
extern int a[5];
b.h:
extern int a[];
x.cc:
#include "a.h"
#include "b.h"
int k = sizeof(a);
On Fri, Jan 22, 2016 at 11:03 AM, Yaron Keren via cfe-commits
<cfe-commits@lists.llvm.org> wrote:
Author: yrnkrn
Date: Fri Jan 22 13:03:27 2016
New Revision: 258524
URL: http://llvm.org/viewvc/llvm-project?rev=258524&view=rev
Log:
Merge templated static member variables, fixes http://llvm.org/pr26179.
Patch by Vassil Vassilev!
Reviewed by Richard Smith.
Added:
cfe/trunk/test/Modules/Inputs/PR26179/
cfe/trunk/test/Modules/Inputs/PR26179/A.h
cfe/trunk/test/Modules/Inputs/PR26179/B.h
cfe/trunk/test/Modules/Inputs/PR26179/basic_string.h
cfe/trunk/test/Modules/Inputs/PR26179/module.modulemap
cfe/trunk/test/Modules/pr26179.cpp
Modified:
cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=258524&r1=258523&r2=258524&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Jan 22 13:03:27
2016
@@ -2595,8 +2595,24 @@ static bool isSameEntity(NamedDecl *X, N
// Variables with the same type and linkage match.
if (VarDecl *VarX = dyn_cast<VarDecl>(X)) {
VarDecl *VarY = cast<VarDecl>(Y);
- return (VarX->getLinkageInternal() == VarY->getLinkageInternal())
&&
- VarX->getASTContext().hasSameType(VarX->getType(),
VarY->getType());
+ if (VarX->getLinkageInternal() == VarY->getLinkageInternal()) {
+ ASTContext &C = VarX->getASTContext();
+ if (C.hasSameType(VarX->getType(), VarY->getType()))
+ return true;
+
+ // We can get decls with different types on the redecl chain.
Eg.
+ // template <typename T> struct S { static T Var[]; }; // #1
+ // template <typename T> T S<T>::Var[sizeof(T)]; // #2
+ // Only? happens when completing an incomplete array type. In
this case
+ // when comparing #1 and #2 we should go through their elements
types.
+ const ArrayType *VarXTy = C.getAsArrayType(VarX->getType());
+ const ArrayType *VarYTy = C.getAsArrayType(VarY->getType());
+ if (!VarXTy || !VarYTy)
+ return false;
+ if (VarXTy->isIncompleteArrayType() ||
VarYTy->isIncompleteArrayType())
+ return C.hasSameType(VarXTy->getElementType(),
VarYTy->getElementType());
+ }
+ return false;
}
// Namespaces with the same name and inlinedness match.
Added: cfe/trunk/test/Modules/Inputs/PR26179/A.h
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/PR26179/A.h?rev=258524&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/PR26179/A.h (added)
+++ cfe/trunk/test/Modules/Inputs/PR26179/A.h Fri Jan 22 13:03:27 2016
@@ -0,0 +1,4 @@
+#include "basic_string.h"
+#include "B.h"
+
+int *p = a;
Added: cfe/trunk/test/Modules/Inputs/PR26179/B.h
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/PR26179/B.h?rev=258524&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/PR26179/B.h (added)
+++ cfe/trunk/test/Modules/Inputs/PR26179/B.h Fri Jan 22 13:03:27 2016
@@ -0,0 +1,2 @@
+#include "basic_string.h"
+extern int a[5];
Added: cfe/trunk/test/Modules/Inputs/PR26179/basic_string.h
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/PR26179/basic_string.h?rev=258524&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/PR26179/basic_string.h (added)
+++ cfe/trunk/test/Modules/Inputs/PR26179/basic_string.h Fri Jan 22
13:03:27 2016
@@ -0,0 +1,14 @@
+#ifndef _GLIBCXX_STRING
+#define _GLIBCXX_STRING 1
+
+template<typename T>
+struct basic_string {
+ static T _S_empty_rep_storage[];
+};
+
+template<typename T>
+T basic_string<T>::_S_empty_rep_storage[sizeof(T)];
+
+extern int a[];
+
+#endif
Added: cfe/trunk/test/Modules/Inputs/PR26179/module.modulemap
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/PR26179/module.modulemap?rev=258524&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/PR26179/module.modulemap (added)
+++ cfe/trunk/test/Modules/Inputs/PR26179/module.modulemap Fri Jan 22
13:03:27 2016
@@ -0,0 +1,9 @@
+module A {
+ header "A.h"
+ export *
+}
+
+module B {
+ header "B.h"
+ export *
+}
Added: cfe/trunk/test/Modules/pr26179.cpp
URL:
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/pr26179.cpp?rev=258524&view=auto
==============================================================================
--- cfe/trunk/test/Modules/pr26179.cpp (added)
+++ cfe/trunk/test/Modules/pr26179.cpp Fri Jan 22 13:03:27 2016
@@ -0,0 +1,7 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -I%S/Inputs/PR26179 -verify %s
+// RUN: %clang_cc1 -fmodules
-fmodule-map-file=%S/Inputs/PR26179/module.modulemap -fmodules-cache-path=%t
-I%S/Inputs/PR26179 -verify %s
+
+#include "A.h"
+
+// expected-no-diagnostics
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
From 61d99ea390f286c2d77e03b13cda114704f1fb70 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassi...@gmail.com>
Date: Fri, 5 Feb 2016 21:15:05 +0100
Subject: [PATCH] [modules] Prefer more complete variable types.
---
lib/Sema/SemaDecl.cpp | 83 +++++++++++++---------
lib/Sema/SemaLookup.cpp | 12 ++++
lib/Serialization/ASTReaderDecl.cpp | 2 +-
test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp | 4 ++
test/Modules/Inputs/PR26179/A.h | 2 -
test/Modules/Inputs/PR26179/B.h | 1 -
test/Modules/Inputs/PR26179/basic_string.h | 2 -
.../Modules/Inputs/merge-incomplete-array-vars/a.h | 4 ++
.../Modules/Inputs/merge-incomplete-array-vars/b.h | 4 ++
.../Inputs/merge-incomplete-array-vars/c1.h | 1 +
.../Inputs/merge-incomplete-array-vars/c2.h | 1 +
.../Modules/Inputs/merge-incomplete-array-vars/d.h | 7 ++
.../merge-incomplete-array-vars/module.modulemap | 5 ++
test/Modules/merge-incomplete-array-vars.cpp | 21 ++++++
14 files changed, 109 insertions(+), 40 deletions(-)
create mode 100644 test/Modules/Inputs/merge-incomplete-array-vars/a.h
create mode 100644 test/Modules/Inputs/merge-incomplete-array-vars/b.h
create mode 100644 test/Modules/Inputs/merge-incomplete-array-vars/c1.h
create mode 100644 test/Modules/Inputs/merge-incomplete-array-vars/c2.h
create mode 100644 test/Modules/Inputs/merge-incomplete-array-vars/d.h
create mode 100644
test/Modules/Inputs/merge-incomplete-array-vars/module.modulemap
create mode 100644 test/Modules/merge-incomplete-array-vars.cpp
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 66c0e05..8b6acbf 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -3245,6 +3245,22 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl
*newMethod,
CheckObjCMethodOverride(newMethod, oldMethod);
}
+static void diagnoseVarDeclTypeMismatch(Sema &S, VarDecl *New, VarDecl* Old) {
+ assert(!S.Context.hasSameType(New->getType(), Old->getType()));
+
+ S.Diag(New->getLocation(), New->isThisDeclarationADefinition()
+ ? diag::err_redefinition_different_type
+ : diag::err_redeclaration_different_type)
+ << New->getDeclName() << New->getType() << Old->getType();
+
+ diag::kind PrevDiag;
+ SourceLocation OldLocation;
+ std::tie(PrevDiag, OldLocation)
+ = getNoteDiagForInvalidRedeclaration(Old, New);
+ S.Diag(OldLocation, PrevDiag);
+ New->setInvalidDecl();
+}
+
/// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and
/// scope as a previous declaration 'Old'. Figure out how to merge their
types,
/// emitting diagnostics as appropriate.
@@ -3271,21 +3287,40 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
// object or function shall be identical, except that declarations for an
// array object can specify array types that differ by the presence or
// absence of a major array bound (8.3.4).
- else if (Old->getType()->isIncompleteArrayType() &&
- New->getType()->isArrayType()) {
+ else if (Old->getType()->isArrayType() && New->getType()->isArrayType()) {
const ArrayType *OldArray = Context.getAsArrayType(Old->getType());
const ArrayType *NewArray = Context.getAsArrayType(New->getType());
- if (Context.hasSameType(OldArray->getElementType(),
- NewArray->getElementType()))
- MergedT = New->getType();
- } else if (Old->getType()->isArrayType() &&
- New->getType()->isIncompleteArrayType()) {
- const ArrayType *OldArray = Context.getAsArrayType(Old->getType());
- const ArrayType *NewArray = Context.getAsArrayType(New->getType());
- if (Context.hasSameType(OldArray->getElementType(),
- NewArray->getElementType()))
- MergedT = Old->getType();
- } else if (New->getType()->isObjCObjectPointerType() &&
+
+ // We are merging a variable declaration New into Old. If it has an array
+ // bound, and that bound differs from Old's bound, we should diagnose the
+ // mismatch.
+ if (!NewArray->isIncompleteArrayType()) {
+ for (VarDecl *PrevVD = Old->getMostRecentDecl(); PrevVD;
+ PrevVD = PrevVD->getPreviousDecl()) {
+ const ArrayType *PrevVDTy =
Context.getAsArrayType(PrevVD->getType());
+ if (PrevVDTy->isIncompleteArrayType())
+ continue;
+
+ if (!Context.hasSameType(NewArray, PrevVDTy))
+ return diagnoseVarDeclTypeMismatch(*this, New, PrevVD);
+ }
+ }
+
+ if (OldArray->isIncompleteArrayType() && NewArray->isArrayType()) {
+ if (Context.hasSameType(OldArray->getElementType(),
+ NewArray->getElementType()))
+ MergedT = New->getType();
+ }
+ // FIXME: Check visibility. New is hidden but has a complete type. If New
+ // has no array bound, it should not inherit one from Old, if Old is not
+ // visible.
+ else if (OldArray->isArrayType() && NewArray->isIncompleteArrayType()) {
+ if (Context.hasSameType(OldArray->getElementType(),
+ NewArray->getElementType()))
+ MergedT = Old->getType();
+ }
+ }
+ else if (New->getType()->isObjCObjectPointerType() &&
Old->getType()->isObjCObjectPointerType()) {
MergedT = Context.mergeObjCGCQualifiers(New->getType(),
Old->getType());
@@ -3311,27 +3346,7 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
New->setType(Context.DependentTy);
return;
}
-
- // FIXME: Even if this merging succeeds, some other non-visible declaration
- // of this variable might have an incompatible type. For instance:
- //
- // extern int arr[];
- // void f() { extern int arr[2]; }
- // void g() { extern int arr[3]; }
- //
- // Neither C nor C++ requires a diagnostic for this, but we should still
try
- // to diagnose it.
- Diag(New->getLocation(), New->isThisDeclarationADefinition()
- ? diag::err_redefinition_different_type
- : diag::err_redeclaration_different_type)
- << New->getDeclName() << New->getType() << Old->getType();
-
- diag::kind PrevDiag;
- SourceLocation OldLocation;
- std::tie(PrevDiag, OldLocation) =
- getNoteDiagForInvalidRedeclaration(Old, New);
- Diag(OldLocation, PrevDiag);
- return New->setInvalidDecl();
+ return diagnoseVarDeclTypeMismatch(*this, New, Old);
}
// Don't actually update the type on the new declaration if the old
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 1d7b15c..7405032 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -419,6 +419,18 @@ static bool isPreferredLookupResult(Sema &S,
Sema::LookupNameKind Kind,
}
}
+ // VarDecl can have incomplete array types, prefer the one with more complete
+ // array type.
+ if (VarDecl *DVD = dyn_cast<VarDecl>(DUnderlying)) {
+ VarDecl *EVD = cast<VarDecl>(EUnderlying);
+ if (EVD->getType()->isIncompleteType() &&
+ !DVD->getType()->isIncompleteType()) {
+ // Prefer the decl with a more complete type if visible.
+ return S.isVisible(DVD);
+ }
+ return false; // Avoid picking up a newer decl, just because it was newer.
+ }
+
// For most kinds of declaration, it doesn't really matter which one we pick.
if (!isa<FunctionDecl>(DUnderlying) && !isa<VarDecl>(DUnderlying)) {
// If the existing declaration is hidden, prefer the new one. Otherwise,
diff --git a/lib/Serialization/ASTReaderDecl.cpp
b/lib/Serialization/ASTReaderDecl.cpp
index 9bcd9a0..feb9d12 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -2604,7 +2604,7 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
// template <typename T> struct S { static T Var[]; }; // #1
// template <typename T> T S<T>::Var[sizeof(T)]; // #2
// Only? happens when completing an incomplete array type. In this case
- // when comparing #1 and #2 we should go through their elements types.
+ // when comparing #1 and #2 we should go through their element type.
const ArrayType *VarXTy = C.getAsArrayType(VarX->getType());
const ArrayType *VarYTy = C.getAsArrayType(VarY->getType());
if (!VarXTy || !VarYTy)
diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
b/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
index 4686b1c..188a0a2 100644
--- a/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
+++ b/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
@@ -207,3 +207,7 @@ namespace use_outside_ns {
int j() { return sizeof(d); }
}
}
+
+extern int arr[];
+void f1() { extern int arr[2]; } // expected-note {{previous}}
+void f2() { extern int arr[3]; } // expected-error {{different type: 'int [3]'
vs 'int [2]'}}
diff --git a/test/Modules/Inputs/PR26179/A.h b/test/Modules/Inputs/PR26179/A.h
index ce95faf..c264f4c 100644
--- a/test/Modules/Inputs/PR26179/A.h
+++ b/test/Modules/Inputs/PR26179/A.h
@@ -1,4 +1,2 @@
#include "basic_string.h"
#include "B.h"
-
-int *p = a;
diff --git a/test/Modules/Inputs/PR26179/B.h b/test/Modules/Inputs/PR26179/B.h
index eb8d1c2..46a109e 100644
--- a/test/Modules/Inputs/PR26179/B.h
+++ b/test/Modules/Inputs/PR26179/B.h
@@ -1,2 +1 @@
#include "basic_string.h"
-extern int a[5];
diff --git a/test/Modules/Inputs/PR26179/basic_string.h
b/test/Modules/Inputs/PR26179/basic_string.h
index afd1e0d..653ce07 100644
--- a/test/Modules/Inputs/PR26179/basic_string.h
+++ b/test/Modules/Inputs/PR26179/basic_string.h
@@ -9,6 +9,4 @@ struct basic_string {
template<typename T>
T basic_string<T>::_S_empty_rep_storage[sizeof(T)];
-extern int a[];
-
#endif
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/a.h
b/test/Modules/Inputs/merge-incomplete-array-vars/a.h
new file mode 100644
index 0000000..4dcf7de
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/a.h
@@ -0,0 +1,4 @@
+extern int a[5];
+extern int aa[55];
+extern int b[];
+extern int bb[];
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/b.h
b/test/Modules/Inputs/merge-incomplete-array-vars/b.h
new file mode 100644
index 0000000..8486136
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/b.h
@@ -0,0 +1,4 @@
+extern int a[];
+extern int aa[];
+extern int b[6];
+extern int bb[65];
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/c1.h
b/test/Modules/Inputs/merge-incomplete-array-vars/c1.h
new file mode 100644
index 0000000..99be230
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/c1.h
@@ -0,0 +1 @@
+extern int c[];
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/c2.h
b/test/Modules/Inputs/merge-incomplete-array-vars/c2.h
new file mode 100644
index 0000000..3fee8c7
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/c2.h
@@ -0,0 +1 @@
+extern int c[1];
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/d.h
b/test/Modules/Inputs/merge-incomplete-array-vars/d.h
new file mode 100644
index 0000000..e24cf76
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/d.h
@@ -0,0 +1,7 @@
+extern int c[];
+void g(int(*)[], int);
+void g(int(*)[1], int) = delete;
+template<typename T> void f(T t) {
+ extern int c[];
+ g(&c, t);
+}
diff --git a/test/Modules/Inputs/merge-incomplete-array-vars/module.modulemap
b/test/Modules/Inputs/merge-incomplete-array-vars/module.modulemap
new file mode 100644
index 0000000..d2b467d
--- /dev/null
+++ b/test/Modules/Inputs/merge-incomplete-array-vars/module.modulemap
@@ -0,0 +1,5 @@
+module A { header "a.h" export * }
+module B { header "b.h" export * }
+
+module C { module C1 { header "c1.h" } module C2 { header "c2.h" } }
+module D { header "d.h" }
diff --git a/test/Modules/merge-incomplete-array-vars.cpp
b/test/Modules/merge-incomplete-array-vars.cpp
new file mode 100644
index 0000000..dd08b64
--- /dev/null
+++ b/test/Modules/merge-incomplete-array-vars.cpp
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -std=c++11 -fmodules -fimplicit-module-maps
-fmodules-cache-path=%t -I%S/Inputs/merge-incomplete-array-vars -verify %s
+
+extern int aa[];
+extern int bb[];
+
+#include "a.h"
+#include "b.h"
+
+// Import the complete type first and the incomplete second, forcing the
+// most recent decl to have an incomplete type.
+int k = sizeof(a);
+int l = sizeof(aa);
+int m = sizeof(b);
+int n = sizeof(bb);
+
+#include "c1.h"
+#include "d.h"
+int size = sizeof(c); // expected-error{{invalid application of 'sizeof' to an
incomplete type 'int []'}}
+void h() { f(0); } // should not produce an error; type of 'c' within 'f'
should not have been updated
+int c[2]; // expected-error {{different type: 'int [2]' vs 'int [1]'}}
//expected-note@c2.h:1 {{previous declaration is here}}
--
2.3.8 (Apple Git-58)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits