Implement conversion from a switch condition with class type to an
integral or enumeration type (vi user-defined conversions). Fixes PR5518.
llvm-svn: 89655
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 9960565..b674a17 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -15,9 +15,11 @@
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
@@ -269,35 +271,7 @@
Action::OwningStmtResult
Sema::ActOnStartOfSwitchStmt(ExprArg cond) {
- Expr *Cond = cond.takeAs<Expr>();
-
- if (getLangOptions().CPlusPlus) {
- // C++ 6.4.2.p2:
- // The condition shall be of integral type, enumeration type, or of a class
- // type for which a single conversion function to integral or enumeration
- // type exists (12.3). If the condition is of class type, the condition is
- // converted by calling that conversion function, and the result of the
- // conversion is used in place of the original condition for the remainder
- // of this section. Integral promotions are performed.
- if (!Cond->isTypeDependent()) {
- QualType Ty = Cond->getType();
-
- // FIXME: Handle class types.
-
- // If the type is wrong a diagnostic will be emitted later at
- // ActOnFinishSwitchStmt.
- if (Ty->isIntegralType() || Ty->isEnumeralType()) {
- // Integral promotions are performed.
- // FIXME: Integral promotions for C++ are not complete.
- UsualUnaryConversions(Cond);
- }
- }
- } else {
- // C99 6.8.4.2p5 - Integer promotions are performed on the controlling expr.
- UsualUnaryConversions(Cond);
- }
-
- SwitchStmt *SS = new (Context) SwitchStmt(Cond);
+ SwitchStmt *SS = new (Context) SwitchStmt(cond.takeAs<Expr>());
getSwitchStack().push_back(SS);
return Owned(SS);
}
@@ -404,8 +378,103 @@
getSwitchStack().pop_back();
Expr *CondExpr = SS->getCond();
+ QualType CondTypeBeforePromotion =
+ GetTypeBeforeIntegralPromotion(CondExpr);
QualType CondType = CondExpr->getType();
+ if (getLangOptions().CPlusPlus) {
+ // C++ 6.4.2.p2:
+ // The condition shall be of integral type, enumeration type, or of a class
+ // type for which a single conversion function to integral or enumeration
+ // type exists (12.3). If the condition is of class type, the condition is
+ // converted by calling that conversion function, and the result of the
+ // conversion is used in place of the original condition for the remainder
+ // of this section. Integral promotions are performed.
+ if (!CondExpr->isTypeDependent()) {
+ llvm::SmallVector<CXXConversionDecl *, 4> ViableConversions;
+ llvm::SmallVector<CXXConversionDecl *, 4> ExplicitConversions;
+ if (const RecordType *RecordTy = CondType->getAs<RecordType>()) {
+ const UnresolvedSet *Conversions
+ = cast<CXXRecordDecl>(RecordTy->getDecl())
+ ->getVisibleConversionFunctions();
+ for (UnresolvedSet::iterator I = Conversions->begin(),
+ E = Conversions->end(); I != E; ++I) {
+ if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(*I))
+ if (Conversion->getConversionType().getNonReferenceType()
+ ->isIntegralType()) {
+ if (Conversion->isExplicit())
+ ExplicitConversions.push_back(Conversion);
+ else
+ ViableConversions.push_back(Conversion);
+ }
+ }
+
+ switch (ViableConversions.size()) {
+ case 0:
+ if (ExplicitConversions.size() == 1) {
+ // The user probably meant to invoke the given explicit
+ // conversion; use it.
+ QualType ConvTy
+ = ExplicitConversions[0]->getConversionType()
+ .getNonReferenceType();
+ std::string TypeStr;
+ ConvTy.getAsStringInternal(TypeStr, Context.PrintingPolicy);
+
+
+ Diag(SwitchLoc, diag::err_switch_explicit_conversion)
+ << CondType << ConvTy << CondExpr->getSourceRange()
+ << CodeModificationHint::CreateInsertion(CondExpr->getLocStart(),
+ "static_cast<" + TypeStr + ">(")
+ << CodeModificationHint::CreateInsertion(
+ PP.getLocForEndOfToken(CondExpr->getLocEnd()),
+ ")");
+ Diag(ExplicitConversions[0]->getLocation(),
+ diag::note_switch_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+
+ // If we aren't in a SFINAE context, build a call to the
+ // explicit conversion function.
+ if (!isSFINAEContext())
+ CondExpr = BuildCXXMemberCallExpr(CondExpr,
+ ExplicitConversions[0]);
+ }
+
+ // We'll complain below about a non-integral condition type.
+ break;
+
+ case 1:
+ // Apply this conversion.
+ CondExpr = BuildCXXMemberCallExpr(CondExpr, ViableConversions[0]);
+ break;
+
+ default:
+ Diag(SwitchLoc, diag::err_switch_multiple_conversions)
+ << CondType << CondExpr->getSourceRange();
+ for (unsigned I = 0, N = ViableConversions.size(); I != N; ++I) {
+ QualType ConvTy
+ = ViableConversions[I]->getConversionType()
+ .getNonReferenceType();
+ Diag(ViableConversions[I]->getLocation(),
+ diag::note_switch_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+ return StmtError();
+ }
+ }
+ CondType = CondExpr->getType();
+
+ if (CondType->isIntegralType() || CondType->isEnumeralType()) {
+ // Integral promotions are performed.
+ UsualUnaryConversions(CondExpr);
+ }
+ }
+ } else {
+ // C99 6.8.4.2p5 - Integer promotions are performed on the controlling expr.
+ UsualUnaryConversions(CondExpr);
+ }
+ CondType = CondExpr->getType();
+ SS->setCond(CondExpr);
+
// C++ 6.4.2.p2:
// Integral promotions are performed (on the switch condition).
//
@@ -413,9 +482,6 @@
// type (before the promotion) doesn't make sense, even when it can
// be represented by the promoted type. Therefore we need to find
// the pre-promotion type of the switch condition.
- QualType CondTypeBeforePromotion =
- GetTypeBeforeIntegralPromotion(CondExpr);
-
if (!CondExpr->isTypeDependent()) {
if (!CondType->isIntegerType()) { // C99 6.8.4.2p1
Diag(SwitchLoc, diag::err_typecheck_statement_requires_integer)