From: Sebastian Redl Date: Sat, 10 Oct 2009 12:04:10 +0000 (+0000) Subject: Implement the core checking for compatible exception specifications in assignment... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2c7588f1260c6655cfb73126b695d2f79ae170bb;p=clang Implement the core checking for compatible exception specifications in assignment and initialization. The exception specification of the assignee must be the same or a subset of the target. In addition, exception specifications on arguments and return types must be equivalent, but this is not implemented yet. This currently produces two diagnostics for every invalid assignment/initialization, due to the diagnostic produced outside PerformImplicitConversion, e.g. in CheckSingleInitializer. I don't know how to suppress this; in any case I think it is the wrong place for a diagnostic, since there are other diagnostics produced inside the function. So I'm leaving it as it is for the moment. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83710 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3b4af8e9b5..f3d69c0035 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -363,6 +363,8 @@ def err_mismatched_exception_spec : Error< def err_override_exception_spec : Error< "exception specification of overriding function is more lax than " "base version">; +def err_incompatible_exception_specs : Error< + "target exception specification is not superset of source">; // C++ access checking def err_class_redeclared_with_different_access : Error< diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 9d35f3dd3f..56eb24edcf 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3519,6 +3519,8 @@ public: bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); + bool CheckExceptionSpecCompatibility(Expr *From, QualType ToType); + bool PerformImplicitConversion(Expr *&From, QualType ToType, const char *Flavor, bool AllowExplicit = false, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index fb0e144036..9a90be4189 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3630,6 +3630,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, CastExpr::CastKind CK = CastExpr::CK_NoOp; if (DerivedToBase) CK = CastExpr::CK_DerivedToBase; + else if(CheckExceptionSpecCompatibility(Init, T1)) + return true; ImpCastExprToType(Init, T1, CK, /*isLvalue=*/true); } } @@ -3703,7 +3705,9 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, cast(Best->Function), Owned(Init)); Init = InitConversion.takeAs(); - + + if (CheckExceptionSpecCompatibility(Init, T1)) + return true; ImpCastExprToType(Init, T1, CastExpr::CK_UserDefinedConversion, /*isLvalue=*/true); } @@ -3792,6 +3796,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, CastExpr::CastKind CK = CastExpr::CK_NoOp; if (DerivedToBase) CK = CastExpr::CK_DerivedToBase; + else if(CheckExceptionSpecCompatibility(Init, T1)) + return true; ImpCastExprToType(Init, T1, CK, /*isLvalue=*/false); } return false; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index d746237563..eb0b6e1eff 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1197,7 +1197,11 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, // Perform the second implicit conversion switch (SCS.Second) { case ICK_Identity: - // Nothing to do. + // If both sides are functions (or pointers/references to them), there could + // be incompatible exception declarations. + if (CheckExceptionSpecCompatibility(From, ToType)) + return true; + // Nothing else to do. break; case ICK_Integral_Promotion: @@ -1235,6 +1239,8 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, CastExpr::CastKind Kind = CastExpr::CK_Unknown; if (CheckMemberPointerConversion(From, ToType, Kind)) return true; + if (CheckExceptionSpecCompatibility(From, ToType)) + return true; ImpCastExprToType(From, ToType, Kind); break; } @@ -1269,6 +1275,38 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, return false; } +bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) +{ + // First we check for applicability. + // Target type must be a function, function pointer or function reference. + if (const PointerType *PtrTy = ToType->getAs()) + ToType = PtrTy->getPointeeType(); + else if (const ReferenceType *RefTy = ToType->getAs()) + ToType = RefTy->getPointeeType(); + + const FunctionProtoType *ToFunc = ToType->getAs(); + if (!ToFunc) + return false; + + // SourceType must be a function or function pointer. + // References are treated as functions. + QualType FromType = From->getType(); + if (const PointerType *PtrTy = FromType->getAs()) + FromType = PtrTy->getPointeeType(); + + const FunctionProtoType *FromFunc = FromType->getAs(); + if (!FromFunc) + return false; + + // Now we've got the correct types on both sides, check their compatibility. + // This means that the source of the conversion can only throw a subset of + // the exceptions of the target, and any exception specs on arguments or + // return types must be equivalent. + return CheckExceptionSpecSubset(diag::err_incompatible_exception_specs, + 0, ToFunc, From->getSourceRange().getBegin(), + FromFunc, SourceLocation()); +} + Sema::OwningExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, SourceLocation KWLoc, SourceLocation LParen, diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 6a5db5f2ad..5c7bbd0441 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1512,6 +1512,9 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID, // FIXME: As usual, we could be more specific in our error messages, but // that better waits until we've got types with source locations. + if (!SubLoc.isValid()) + SubLoc = SuperLoc; + // If superset contains everything, we're done. if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec()) return false; @@ -1519,7 +1522,8 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID, // It does not. If the subset contains everything, we've failed. if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) { Diag(SubLoc, DiagID); - Diag(SuperLoc, NoteID); + if (NoteID != 0) + Diag(SuperLoc, NoteID); return true; } @@ -1584,7 +1588,8 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID, } if (!Contained) { Diag(SubLoc, DiagID); - Diag(SuperLoc, NoteID); + if (NoteID != 0) + Diag(SuperLoc, NoteID); return true; } } diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp index 9d656ad212..cb4cbf4a70 100644 --- a/test/SemaCXX/exception-spec.cpp +++ b/test/SemaCXX/exception-spec.cpp @@ -134,25 +134,25 @@ void fnptrs() { // Assignment and initialization of function pointers. void (*t1)() throw() = &s1; // valid - t1 = &s2; // invalid - t1 = &s3; // invalid - void (&t2)() throw() = s2; // invalid + t1 = &s2; // expected-error {{not superset}} expected-error {{incompatible type}} + t1 = &s3; // expected-error {{not superset}} expected-error {{incompatible type}} + void (&t2)() throw() = s2; // expected-error {{not superset}} void (*t3)() throw(int) = &s2; // valid void (*t4)() throw(A) = &s1; // valid t4 = &s3; // valid t4 = &s4; // valid - t4 = &s5; // invalid + t4 = &s5; // expected-error {{not superset}} expected-error {{incompatible type}} void (*t5)() = &s1; // valid t5 = &s2; // valid t5 = &s6; // valid t5 = &s7; // valid - t1 = t3; // invalid + t1 = t3; // expected-error {{not superset}} expected-error {{incompatible type}} t3 = t1; // valid void (*t6)() throw(B1); - t6 = t4; // invalid + t6 = t4; // expected-error {{not superset}} expected-error {{incompatible type}} t4 = t6; // valid t5 = t1; // valid - t1 = t5; // invalid + t1 = t5; // expected-error {{not superset}} expected-error {{incompatible type}} // return types and arguments must match exactly, no inheritance allowed void (*(*t7)())() throw(B1) = &s8; // valid