/// TryStaticMemberPointerUpcast - Tests whether a conversion according to /// C++ 5.2.9p9 is valid: /// /// An rvalue of type "pointer to member of D of type cv1 T" can be /// converted to an rvalue of type "pointer to member of B of type cv2 T", /// where B is a base class of D [...]. /// TryCastResult TryStaticMemberPointerUpcast(Sema &Self, QualType SrcType, QualType DestType, bool CStyle, const SourceRange &OpRange, unsigned &msg) { const MemberPointerType *DestMemPtr = DestType->getAs<MemberPointerType>(); if (!DestMemPtr) return TC_NotApplicable; const MemberPointerType *SrcMemPtr = SrcType->getAs<MemberPointerType>(); if (!SrcMemPtr) { msg = diag::err_bad_static_cast_member_pointer_nonmp; return TC_NotApplicable; } // T == T, modulo cv if (Self.Context.getCanonicalType( SrcMemPtr->getPointeeType().getUnqualifiedType()) != Self.Context.getCanonicalType(DestMemPtr->getPointeeType(). getUnqualifiedType())) return TC_NotApplicable; // B base of D QualType SrcClass(SrcMemPtr->getClass(), 0); QualType DestClass(DestMemPtr->getClass(), 0); BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/!CStyle, /*DetectVirtual=*/true); if (!Self.IsDerivedFrom(SrcClass, DestClass, Paths)) { return TC_NotApplicable; } // B is a base of D. But is it an allowed base? If not, it's a hard error. if (Paths.isAmbiguous(DestClass)) { Paths.clear(); Paths.setRecordingPaths(true); bool StillOkay = Self.IsDerivedFrom(SrcClass, DestClass, Paths); assert(StillOkay); StillOkay = StillOkay; std::string PathDisplayStr = Self.getAmbiguousPathsDisplayString(Paths); Self.Diag(OpRange.getBegin(), diag::err_ambiguous_memptr_conv) << 1 << SrcClass << DestClass << PathDisplayStr << OpRange; msg = 0; return TC_Failed; } if (const RecordType *VBase = Paths.getDetectedVirtual()) { Self.Diag(OpRange.getBegin(), diag::err_memptr_conv_via_virtual) << SrcClass << DestClass << QualType(VBase, 0) << OpRange; msg = 0; return TC_Failed; } if (!CStyle && Self.CheckBaseClassAccess(DestType, SrcType, diag::err_downcast_from_inaccessible_base, Paths, OpRange.getBegin(), DeclarationName())) { msg = 0; return TC_Failed; } return TC_Success; }
/// TryStaticMemberPointerUpcast - Tests whether a conversion according to /// C++ 5.2.9p9 is valid: /// /// An rvalue of type "pointer to member of D of type cv1 T" can be /// converted to an rvalue of type "pointer to member of B of type cv2 T", /// where B is a base class of D [...]. /// TryStaticCastResult TryStaticMemberPointerUpcast(Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange) { const MemberPointerType *SrcMemPtr = SrcType->getAsMemberPointerType(); if (!SrcMemPtr) return TSC_NotApplicable; const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType(); if (!DestMemPtr) return TSC_NotApplicable; // T == T, modulo cv if (Self.Context.getCanonicalType( SrcMemPtr->getPointeeType().getUnqualifiedType()) != Self.Context.getCanonicalType(DestMemPtr->getPointeeType(). getUnqualifiedType())) return TSC_NotApplicable; // B base of D QualType SrcClass(SrcMemPtr->getClass(), 0); QualType DestClass(DestMemPtr->getClass(), 0); BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false, /*DetectVirtual=*/true); if (!Self.IsDerivedFrom(SrcClass, DestClass, Paths)) { return TSC_NotApplicable; } // B is a base of D. But is it an allowed base? If not, it's a hard error. if (Paths.isAmbiguous(DestClass)) { Paths.clear(); Paths.setRecordingPaths(true); bool StillOkay = Self.IsDerivedFrom(SrcClass, DestClass, Paths); assert(StillOkay); StillOkay = StillOkay; std::string PathDisplayStr = Self.getAmbiguousPathsDisplayString(Paths); Self.Diag(OpRange.getBegin(), diag::err_ambiguous_memptr_conv) << 1 << SrcClass << DestClass << PathDisplayStr << OpRange; return TSC_Failed; } if (const RecordType *VBase = Paths.getDetectedVirtual()) { Self.Diag(OpRange.getBegin(), diag::err_memptr_conv_via_virtual) << SrcClass << DestClass << QualType(VBase, 0) << OpRange; return TSC_Failed; } // FIXME: Test accessibility. return TSC_Success; }
/// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid. /// Refer to C++ 5.2.7 for details. Dynamic casts are used mostly for runtime- /// checked downcasts in class hierarchies. void CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType, const SourceRange &OpRange, const SourceRange &DestRange) { QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType(); DestType = Self.Context.getCanonicalType(DestType); // C++ 5.2.7p1: T shall be a pointer or reference to a complete class type, // or "pointer to cv void". QualType DestPointee; const PointerType *DestPointer = DestType->getAsPointerType(); const ReferenceType *DestReference = DestType->getAsReferenceType(); if (DestPointer) { DestPointee = DestPointer->getPointeeType(); } else if (DestReference) { DestPointee = DestReference->getPointeeType(); } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ref_or_ptr) << OrigDestType << DestRange; return; } const RecordType *DestRecord = DestPointee->getAsRecordType(); if (DestPointee->isVoidType()) { assert(DestPointer && "Reference to void is not possible"); } else if (DestRecord) { if (Self.RequireCompleteType(OpRange.getBegin(), DestPointee, diag::err_bad_dynamic_cast_incomplete, DestRange)) return; } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class) << DestPointee.getUnqualifiedType() << DestRange; return; } // C++0x 5.2.7p2: If T is a pointer type, v shall be an rvalue of a pointer to // complete class type, [...]. If T is an lvalue reference type, v shall be // an lvalue of a complete class type, [...]. If T is an rvalue reference // type, v shall be an expression having a complete effective class type, // [...] QualType SrcType = Self.Context.getCanonicalType(OrigSrcType); QualType SrcPointee; if (DestPointer) { if (const PointerType *SrcPointer = SrcType->getAsPointerType()) { SrcPointee = SrcPointer->getPointeeType(); } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ptr) << OrigSrcType << SrcExpr->getSourceRange(); return; } } else if (DestReference->isLValueReferenceType()) { if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) { Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue) << "dynamic_cast" << OrigDestType << OpRange; } SrcPointee = SrcType; } else { SrcPointee = SrcType; } const RecordType *SrcRecord = SrcPointee->getAsRecordType(); if (SrcRecord) { if (Self.RequireCompleteType(OpRange.getBegin(), SrcPointee, diag::err_bad_dynamic_cast_incomplete, SrcExpr->getSourceRange())) return; } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class) << SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange(); return; } assert((DestPointer || DestReference) && "Bad destination non-ptr/ref slipped through."); assert((DestRecord || DestPointee->isVoidType()) && "Bad destination pointee slipped through."); assert(SrcRecord && "Bad source pointee slipped through."); // C++ 5.2.7p1: The dynamic_cast operator shall not cast away constness. if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) { Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away) << "dynamic_cast" << OrigDestType << OrigSrcType << OpRange; return; } // C++ 5.2.7p3: If the type of v is the same as the required result type, // [except for cv]. if (DestRecord == SrcRecord) { return; } // C++ 5.2.7p5 // Upcasts are resolved statically. if (DestRecord && Self.IsDerivedFrom(SrcPointee, DestPointee)) { Self.CheckDerivedToBaseConversion(SrcPointee, DestPointee, OpRange.getBegin(), OpRange); // Diagnostic already emitted on error. return; } // C++ 5.2.7p6: Otherwise, v shall be [polymorphic]. const RecordDecl *SrcDecl = SrcRecord->getDecl()->getDefinition(Self.Context); assert(SrcDecl && "Definition missing"); if (!cast<CXXRecordDecl>(SrcDecl)->isPolymorphic()) { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_polymorphic) << SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange(); } // Done. Everything else is run-time checks. }
/// TryStaticDowncast - Common functionality of TryStaticReferenceDowncast and /// TryStaticPointerDowncast. Tests whether a static downcast from SrcType to /// DestType, both of which must be canonical, is possible and allowed. TryStaticCastResult TryStaticDowncast(Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange, QualType OrigSrcType, QualType OrigDestType) { // Downcast can only happen in class hierarchies, so we need classes. if (!DestType->isRecordType() || !SrcType->isRecordType()) { return TSC_NotApplicable; } BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false, /*DetectVirtual=*/true); if (!Self.IsDerivedFrom(DestType, SrcType, Paths)) { return TSC_NotApplicable; } // Target type does derive from source type. Now we're serious. If an error // appears now, it's not ignored. // This may not be entirely in line with the standard. Take for example: // struct A {}; // struct B : virtual A { // B(A&); // }; // // void f() // { // (void)static_cast<const B&>(*((A*)0)); // } // As far as the standard is concerned, p5 does not apply (A is virtual), so // p2 should be used instead - "const B& t(*((A*)0));" is perfectly valid. // However, both GCC and Comeau reject this example, and accepting it would // mean more complex code if we're to preserve the nice error message. // FIXME: Being 100% compliant here would be nice to have. // Must preserve cv, as always. if (!DestType.isAtLeastAsQualifiedAs(SrcType)) { Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away) << "static_cast" << OrigDestType << OrigSrcType << OpRange; return TSC_Failed; } if (Paths.isAmbiguous(SrcType.getUnqualifiedType())) { // This code is analoguous to that in CheckDerivedToBaseConversion, except // that it builds the paths in reverse order. // To sum up: record all paths to the base and build a nice string from // them. Use it to spice up the error message. Paths.clear(); Paths.setRecordingPaths(true); Self.IsDerivedFrom(DestType, SrcType, Paths); std::string PathDisplayStr; std::set<unsigned> DisplayedPaths; for (BasePaths::paths_iterator Path = Paths.begin(); Path != Paths.end(); ++Path) { if (DisplayedPaths.insert(Path->back().SubobjectNumber).second) { // We haven't displayed a path to this particular base // class subobject yet. PathDisplayStr += "\n "; for (BasePath::const_reverse_iterator Element = Path->rbegin(); Element != Path->rend(); ++Element) PathDisplayStr += Element->Base->getType().getAsString() + " -> "; PathDisplayStr += DestType.getAsString(); } } Self.Diag(OpRange.getBegin(), diag::err_ambiguous_base_to_derived_cast) << SrcType.getUnqualifiedType() << DestType.getUnqualifiedType() << PathDisplayStr << OpRange; return TSC_Failed; } if (Paths.getDetectedVirtual() != 0) { QualType VirtualBase(Paths.getDetectedVirtual(), 0); Self.Diag(OpRange.getBegin(), diag::err_static_downcast_via_virtual) << OrigSrcType << OrigDestType << VirtualBase << OpRange; return TSC_Failed; } // FIXME: Test accessibility. return TSC_Success; }