diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 7 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 130 | ||||
-rw-r--r-- | test/FixIt/fixit-cxx0x.cpp | 14 | ||||
-rw-r--r-- | test/SemaCXX/switch-0x.cpp | 11 | ||||
-rw-r--r-- | test/SemaCXX/switch.cpp | 20 |
5 files changed, 150 insertions, 32 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 399cf37cca..95f65805d6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2292,6 +2292,13 @@ def err_typecheck_statement_requires_integer : Error< "statement requires expression of integer type (%0 invalid)">; def err_multiple_default_labels_defined : Error< "multiple default labels in one switch">; +def err_switch_multiple_conversions : Error< + "multiple conversions from switch condition type %0 to an integral or " + "enumeration type">; +def note_switch_conversion : Note< + "conversion to %select{integral|enumeration}0 type %1">; +def err_switch_explicit_conversion : Error< + "switch condition type %0 requires explicit conversion to %1">; def warn_empty_if_body : Warning< "if statement has empty body">, InGroup<EmptyBody>; def err_va_start_used_in_non_variadic_function : Error< diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 9960565877..b674a1738c 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/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 @@ Sema::ActOnIfStmt(SourceLocation IfLoc, FullExprArg CondVal, 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 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch, 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 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch, // 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) diff --git a/test/FixIt/fixit-cxx0x.cpp b/test/FixIt/fixit-cxx0x.cpp new file mode 100644 index 0000000000..2c783bc2e3 --- /dev/null +++ b/test/FixIt/fixit-cxx0x.cpp @@ -0,0 +1,14 @@ +/* RUN: clang-cc -std=c++0x -fixit %s -o - | clang-cc -x c++ -std=c++0x - + */ + +/* This is a test of the various code modification hints that only + apply in C++0x. */ +struct A { + explicit operator int(); // expected-note{{conversion to integral type}} +}; + +void x() { + switch(A()) { // expected-error{{explicit conversion to}} + } +} + diff --git a/test/SemaCXX/switch-0x.cpp b/test/SemaCXX/switch-0x.cpp new file mode 100644 index 0000000000..c1f6bd9d19 --- /dev/null +++ b/test/SemaCXX/switch-0x.cpp @@ -0,0 +1,11 @@ +// RUN: clang-cc -fsyntax-only -verify -std=c++0x %s + +// PR5518 +struct A { + explicit operator int(); // expected-note{{conversion to integral type}} +}; + +void x() { + switch(A()) { // expected-error{{explicit conversion to}} + } +} diff --git a/test/SemaCXX/switch.cpp b/test/SemaCXX/switch.cpp index b22adb7495..2f2f2a9587 100644 --- a/test/SemaCXX/switch.cpp +++ b/test/SemaCXX/switch.cpp @@ -13,3 +13,23 @@ void test() { break; } } + +// PR5518 +struct A { + operator int(); // expected-note{{conversion to integral type}} +}; + +void x() { + switch(A()) { + } +} + +enum E { e1, e2 }; +struct B : A { + operator E() const; // expected-note{{conversion to enumeration type}} +}; + +void x2() { + switch (B()) { // expected-error{{multiple conversions}} + } +} |