From: Argyrios Kyrtzidis Date: Wed, 10 Sep 2008 02:17:11 +0000 (+0000) Subject: Implement Sema support for the 'condition' part of C++ selection-statements and itera... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5921093cf1c2e9a8bd1a22b6f612e551bae7476b;p=clang Implement Sema support for the 'condition' part of C++ selection-statements and iteration-statements (if/switch/while/for). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56044 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index dffbfad620..7326fb1207 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -985,6 +985,14 @@ DIAG(err_value_init_for_array_type, ERROR, // Temporary DIAG(err_unsupported_class_constructor, ERROR, "class constructors are not supported yet") +DIAG(err_invalid_use_of_function_type, ERROR, + "a function type is not allowed here") +DIAG(err_invalid_use_of_array_type, ERROR, + "an array type is not allowed here") +DIAG(err_type_defined_in_condition, ERROR, + "types may not be defined in conditions") +DIAG(err_typecheck_bool_condition, ERROR, + "expression must have bool type (or be convertible to bool) ('%0' invalid)") // assignment related diagnostics (also for argument passing, returning, etc). diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index f7c9a997b7..3fc152a5f0 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -615,6 +615,15 @@ public: SourceLocation *CommaLocs, SourceLocation RParenLoc); + /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a + /// C++ if/switch/while/for statement. + /// e.g: "if (int x = f()) {...}" + virtual ExprResult ActOnCXXConditionDeclarationExpr(Scope *S, + SourceLocation StartLoc, + Declarator &D, + SourceLocation EqualLoc, + ExprTy *AssignExprVal); + // ParseObjCStringLiteral - Parse Objective-C string literals. virtual ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ExprTy **Strings, @@ -955,6 +964,9 @@ private: // returns true if there were any incompatible arguments. bool CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, ObjCMethodDecl *Method); + + /// CheckCXXBooleanCondition - Returns true if conversion to bool is invalid. + bool CheckCXXBooleanCondition(Expr *&CondExpr); /// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have /// the specified width and sign. If an overflow occurs, detect it and emit diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1c723e69b3..a7e36c95fc 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -14,6 +14,7 @@ #include "Sema.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ASTContext.h" +#include "clang/Parse/DeclSpec.h" #include "clang/Basic/Diagnostic.h" using namespace clang; @@ -132,3 +133,72 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, return new CXXZeroInitValueExpr(Ty, TyBeginLoc, RParenLoc); } + + +/// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a +/// C++ if/switch/while/for statement. +/// e.g: "if (int x = f()) {...}" +Action::ExprResult +Sema::ActOnCXXConditionDeclarationExpr(Scope *S, SourceLocation StartLoc, + Declarator &D, + SourceLocation EqualLoc, + ExprTy *AssignExprVal) { + assert(AssignExprVal && "Null assignment expression"); + + // C++ 6.4p2: + // The declarator shall not specify a function or an array. + // The type-specifier-seq shall not contain typedef and shall not declare a + // new class or enumeration. + + assert(D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && + "Parser allowed 'typedef' as storage class of condition decl."); + + QualType Ty = GetTypeForDeclarator(D, S); + + if (Ty->isFunctionType()) { // The declarator shall not specify a function... + // We exit without creating a CXXConditionDeclExpr because a FunctionDecl + // would be created and CXXConditionDeclExpr wants a VarDecl. + return Diag(StartLoc, diag::err_invalid_use_of_function_type, + SourceRange(StartLoc, EqualLoc)); + } else if (Ty->isArrayType()) { // ...or an array. + Diag(StartLoc, diag::err_invalid_use_of_array_type, + SourceRange(StartLoc, EqualLoc)); + } else if (const RecordType *RT = Ty->getAsRecordType()) { + RecordDecl *RD = RT->getDecl(); + // The type-specifier-seq shall not declare a new class... + if (RD->isDefinition() && (RD->getIdentifier() == 0 || S->isDeclScope(RD))) + Diag(RD->getLocation(), diag::err_type_defined_in_condition); + } else if (const EnumType *ET = Ty->getAsEnumType()) { + EnumDecl *ED = ET->getDecl(); + // ...or enumeration. + if (ED->isDefinition() && (ED->getIdentifier() == 0 || S->isDeclScope(ED))) + Diag(ED->getLocation(), diag::err_type_defined_in_condition); + } + + DeclTy *Dcl = ActOnDeclarator(S, D, 0); + if (!Dcl) + return true; + AddInitializerToDecl(Dcl, AssignExprVal); + + return new CXXConditionDeclExpr(StartLoc, EqualLoc, + cast(static_cast(Dcl))); +} + +/// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid. +bool Sema::CheckCXXBooleanCondition(Expr *&CondExpr) { + // C++ 6.4p4: + // The value of a condition that is an initialized declaration in a statement + // other than a switch statement is the value of the declared variable + // implicitly converted to type bool. If that conversion is ill-formed, the + // program is ill-formed. + // The value of a condition that is an expression is the value of the + // expression, implicitly converted to bool. + // + QualType Ty = CondExpr->getType(); // Save the type. + AssignConvertType + ConvTy = CheckSingleAssignmentConstraints(Context.BoolTy, CondExpr); + if (ConvTy == Incompatible) + return Diag(CondExpr->getLocStart(), diag::err_typecheck_bool_condition, + Ty.getAsString(), CondExpr->getSourceRange()); + return false; +} diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 98efc8a907..706bcddff2 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -185,7 +185,10 @@ Sema::ActOnIfStmt(SourceLocation IfLoc, ExprTy *CondVal, DefaultFunctionArrayConversion(condExpr); QualType condType = condExpr->getType(); - if (!condType->isScalarType()) // C99 6.8.4.1p1 + if (getLangOptions().CPlusPlus) { + if (CheckCXXBooleanCondition(condExpr)) // C++ 6.4p4 + return true; + } else if (!condType->isScalarType()) // C99 6.8.4.1p1 return Diag(IfLoc, diag::err_typecheck_statement_requires_scalar, condType.getAsString(), condExpr->getSourceRange()); @@ -205,8 +208,30 @@ Action::StmtResult Sema::ActOnStartOfSwitchStmt(ExprTy *cond) { Expr *Cond = static_cast(cond); - // C99 6.8.4.2p5 - Integer promotions are performed on the controlling expr. - UsualUnaryConversions(Cond); + 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. + + 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 SwitchStmt(Cond); SwitchStack.push_back(SS); @@ -486,7 +511,10 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, ExprTy *Cond, StmtTy *Body) { DefaultFunctionArrayConversion(condExpr); QualType condType = condExpr->getType(); - if (!condType->isScalarType()) // C99 6.8.5p2 + if (getLangOptions().CPlusPlus) { + if (CheckCXXBooleanCondition(condExpr)) // C++ 6.4p4 + return true; + } else if (!condType->isScalarType()) // C99 6.8.5p2 return Diag(WhileLoc, diag::err_typecheck_statement_requires_scalar, condType.getAsString(), condExpr->getSourceRange()); @@ -518,24 +546,29 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Expr *Third = static_cast(third); Stmt *Body = static_cast(body); - if (DeclStmt *DS = dyn_cast_or_null(First)) { - // C99 6.8.5p3: The declaration part of a 'for' statement shall only declare - // identifiers for objects having storage class 'auto' or 'register'. - for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); - DI!=DE; ++DI) { - VarDecl *VD = dyn_cast(*DI); - if (VD && VD->isBlockVarDecl() && !VD->hasLocalStorage()) - VD = 0; - if (VD == 0) - Diag((*DI)->getLocation(), diag::err_non_variable_decl_in_for); - // FIXME: mark decl erroneous! + if (!getLangOptions().CPlusPlus) { + if (DeclStmt *DS = dyn_cast_or_null(First)) { + // C99 6.8.5p3: The declaration part of a 'for' statement shall only declare + // identifiers for objects having storage class 'auto' or 'register'. + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); + DI!=DE; ++DI) { + VarDecl *VD = dyn_cast(*DI); + if (VD && VD->isBlockVarDecl() && !VD->hasLocalStorage()) + VD = 0; + if (VD == 0) + Diag((*DI)->getLocation(), diag::err_non_variable_decl_in_for); + // FIXME: mark decl erroneous! + } } } if (Second) { DefaultFunctionArrayConversion(Second); QualType SecondType = Second->getType(); - if (!SecondType->isScalarType()) // C99 6.8.5p2 + if (getLangOptions().CPlusPlus) { + if (CheckCXXBooleanCondition(Second)) // C++ 6.4p4 + return true; + } else if (!SecondType->isScalarType()) // C99 6.8.5p2 return Diag(ForLoc, diag::err_typecheck_statement_requires_scalar, SecondType.getAsString(), Second->getSourceRange()); } diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp new file mode 100644 index 0000000000..e535a00f41 --- /dev/null +++ b/test/SemaCXX/condition.cpp @@ -0,0 +1,33 @@ +// RUN: clang -fsyntax-only -verify %s + +void test() { + int a; + if (a) ++a; + if (int x=0) ++x; + + typedef int arr[10]; + while (arr x=0) ; // expected-error: {{an array type is not allowed here}} expected-error: {{initialization with "{...}" expected for array}} + while (int f()=0) ; // expected-error: {{a function type is not allowed here}} + + struct S {} s; + if (s) ++a; // expected-error: {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} + while (struct S x=s) ; // expected-error: {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} + switch (s) {} // expected-error: {{statement requires expression of integer type ('struct S' invalid)}} + + while (struct S {} x=0) ; // expected-error: {{types may not be defined in conditions}} expected-error: {{incompatible type}} expected-error: {{expression must have bool type}} + while (struct {} x=0) ; // expected-error: {{types may not be defined in conditions}} expected-error: {{incompatible type}} expected-error: {{expression must have bool type}} + switch (enum {E} x=0) ; // expected-error: {{types may not be defined in conditions}} + + if (int x=0) { // expected-error: {{previous definition is here}} + int x; // expected-error: {{redefinition of 'x'}} + } + else + int x; // expected-error: {{redefinition of 'x'}} + while (int x=0) int x; // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + while (int x=0) { int x; } // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + for (int x; int x=0; ) ; // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + for (int x; ; ) int x; // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + for (; int x=0; ) int x; // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + for (; int x=0; ) { int x; } // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} + switch (int x=0) { default: int x; } // expected-error: {{redefinition of 'x'}} expected-error: {{previous definition is here}} +}