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(); +} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits