[clang] 2bf6e44 - Attempt to complete an incomplete expression type when considering a
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 8 15:19:39 PST 2021
Author: Richard Smith
Date: 2021-01-08T15:19:28-08:00
New Revision: 2bf6e443e54604c7818c4d1a1837f3d091023270
URL: https://github.com/llvm/llvm-project/commit/2bf6e443e54604c7818c4d1a1837f3d091023270
DIFF: https://github.com/llvm/llvm-project/commit/2bf6e443e54604c7818c4d1a1837f3d091023270.diff
LOG: Attempt to complete an incomplete expression type when considering a
reference binding to an expression.
We need to know the array bound in order to determine whether the
parameter type is reference-compatible with the argument type, so we
need to trigger instantiation in this case.
Added:
Modified:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaType.cpp
clang/test/SemaTemplate/instantiate-static-var.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index acc3184aea97..1d79e5716ef7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2139,6 +2139,16 @@ class Sema final {
return RequireCompleteType(Loc, T, CompleteTypeKind::Normal, Diagnoser);
}
+ /// Get the type of expression E, triggering instantiation to complete the
+ /// type if necessary -- that is, if the expression refers to a templated
+ /// static data member of incomplete array type.
+ ///
+ /// May still return an incomplete type if instantiation was not possible or
+ /// if the type is incomplete for a
diff erent reason. Use
+ /// RequireCompleteExprType instead if a diagnostic is expected for an
+ /// incomplete expression type.
+ QualType getCompletedType(Expr *E);
+
void completeExprArrayBound(Expr *E);
bool RequireCompleteExprType(Expr *E, CompleteTypeKind Kind,
TypeDiagnoser &Diagnoser);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 38f6a5975ea3..f4493d84238d 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4264,7 +4264,7 @@ static void TryReferenceListInitialization(Sema &S,
// bind to that.
if (InitList->getNumInits() == 1) {
Expr *Initializer = InitList->getInit(0);
- QualType cv2T2 = Initializer->getType();
+ QualType cv2T2 = S.getCompletedType(Initializer);
Qualifiers T2Quals;
QualType T2 = S.Context.getUnqualifiedArrayType(cv2T2, T2Quals);
@@ -4700,7 +4700,7 @@ static void TryReferenceInitialization(Sema &S,
QualType cv1T1 = DestType->castAs<ReferenceType>()->getPointeeType();
Qualifiers T1Quals;
QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals);
- QualType cv2T2 = Initializer->getType();
+ QualType cv2T2 = S.getCompletedType(Initializer);
Qualifiers T2Quals;
QualType T2 = S.Context.getUnqualifiedArrayType(cv2T2, T2Quals);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 4a3b64cf5425..ee4316e7a632 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3861,10 +3861,8 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(
if (ParamRefType) {
// If the argument has incomplete array type, try to complete its type.
- if (ArgType->isIncompleteArrayType()) {
- S.completeExprArrayBound(Arg);
- ArgType = Arg->getType();
- }
+ if (ArgType->isIncompleteArrayType())
+ ArgType = S.getCompletedType(Arg);
// C++1z [temp.deduct.call]p3:
// If P is a forwarding reference and the argument is an lvalue, the type
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index fe775b82a1d6..8f2dd384b43b 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8276,6 +8276,20 @@ void Sema::completeExprArrayBound(Expr *E) {
}
}
+QualType Sema::getCompletedType(Expr *E) {
+ // Incomplete array types may be completed by the initializer attached to
+ // their definitions. For static data members of class templates and for
+ // variable templates, we need to instantiate the definition to get this
+ // initializer and complete the type.
+ if (E->getType()->isIncompleteArrayType())
+ completeExprArrayBound(E);
+
+ // FIXME: Are there other cases which require instantiating something other
+ // than the type to complete the type of an expression?
+
+ return E->getType();
+}
+
/// Ensure that the type of the given expression is complete.
///
/// This routine checks whether the expression \p E has a complete type. If the
@@ -8293,21 +8307,8 @@ void Sema::completeExprArrayBound(Expr *E) {
/// otherwise.
bool Sema::RequireCompleteExprType(Expr *E, CompleteTypeKind Kind,
TypeDiagnoser &Diagnoser) {
- QualType T = E->getType();
-
- // Incomplete array types may be completed by the initializer attached to
- // their definitions. For static data members of class templates and for
- // variable templates, we need to instantiate the definition to get this
- // initializer and complete the type.
- if (T->isIncompleteArrayType()) {
- completeExprArrayBound(E);
- T = E->getType();
- }
-
- // FIXME: Are there other cases which require instantiating something other
- // than the type to complete the type of an expression?
-
- return RequireCompleteType(E->getExprLoc(), T, Kind, Diagnoser);
+ return RequireCompleteType(E->getExprLoc(), getCompletedType(E), Kind,
+ Diagnoser);
}
bool Sema::RequireCompleteExprType(Expr *E, unsigned DiagID) {
diff --git a/clang/test/SemaTemplate/instantiate-static-var.cpp b/clang/test/SemaTemplate/instantiate-static-var.cpp
index 648ee4153fdc..7a6de7896cba 100644
--- a/clang/test/SemaTemplate/instantiate-static-var.cpp
+++ b/clang/test/SemaTemplate/instantiate-static-var.cpp
@@ -135,3 +135,33 @@ MyString StaticVarWithTypedefString<T>::str = "";
void testStaticVarWithTypedefString() {
(void)StaticVarWithTypedefString<int>::str;
}
+
+namespace ArrayBound {
+#if __cplusplus >= 201103L
+ template<typename T> void make_unique(T &&);
+
+ template<typename> struct Foo {
+ static constexpr char kMessage[] = "abc";
+ static void f() { make_unique(kMessage); }
+ static void g1() { const char (&ref)[4] = kMessage; } // OK
+ // We can diagnose this prior to instantiation because kMessage is not type-dependent.
+ static void g2() { const char (&ref)[5] = kMessage; } // expected-error {{could not bind}}
+ };
+ template void Foo<int>::f();
+#endif
+
+ template<typename> struct Bar {
+ static const char kMessage[];
+ // Here, kMessage is type-dependent, so we don't diagnose until
+ // instantiation.
+ static void g1() { const char (&ref)[4] = kMessage; } // expected-error {{could not bind to an lvalue of type 'const char [5]'}}
+ static void g2() { const char (&ref)[5] = kMessage; } // expected-error {{could not bind to an lvalue of type 'const char [4]'}}
+ };
+ template<typename T> const char Bar<T>::kMessage[] = "foo";
+ template void Bar<int>::g1();
+ template void Bar<int>::g2(); // expected-note {{in instantiation of}}
+
+ template<> const char Bar<char>::kMessage[] = "foox";
+ template void Bar<char>::g1(); // expected-note {{in instantiation of}}
+ template void Bar<char>::g2();
+}
More information about the cfe-commits
mailing list