First non-embarrassing cut at checking for ambiguous derived-to-base
conversions.
Added PerformImplicitConversion, which follows an implicit conversion sequence
computed by TryCopyInitialization and actually performs the implicit
conversions, including the extra check for ambiguity mentioned above.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58071 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaInherit.cpp b/lib/Sema/SemaInherit.cpp
index 6890dbf..256ee24 100644
--- a/lib/Sema/SemaInherit.cpp
+++ b/lib/Sema/SemaInherit.cpp
@@ -14,19 +14,56 @@
//===----------------------------------------------------------------------===//
#include "Sema.h"
+#include "SemaInherit.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeOrdering.h"
+#include "clang/Basic/Diagnostic.h"
+#include <memory>
+#include <set>
+#include <string>
namespace clang {
+/// isAmbiguous - Determines whether the set of paths provided is
+/// ambiguous, i.e., there are two or more paths that refer to
+/// different base class subobjects of the same type. BaseType must be
+/// an unqualified, canonical class type.
+bool BasePaths::isAmbiguous(QualType BaseType) {
+ std::pair<bool, unsigned>& Subobjects = ClassSubobjects[BaseType];
+ return Subobjects.second + (Subobjects.first? 1 : 0) > 1;
+}
+
+/// clear - Clear out all prior path information.
+void BasePaths::clear() {
+ Paths.clear();
+ ClassSubobjects.clear();
+ ScratchPath.clear();
+}
+
+/// IsDerivedFrom - Determine whether the class type Derived is
+/// derived from the class type Base, ignoring qualifiers on Base and
+/// Derived. This routine does not assess whether an actual conversion
+/// from a Derived* to a Base* is legal, because it does not account
+/// for ambiguous conversions or conversions to private/protected bases.
+bool Sema::IsDerivedFrom(QualType Derived, QualType Base) {
+ BasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false);
+ return IsDerivedFrom(Derived, Base, Paths);
+}
+
/// IsDerivedFrom - Determine whether the class type Derived is
/// derived from the class type Base, ignoring qualifiers on Base and
/// Derived. This routine does not assess whether an actual conversion
/// from a Derived* to a Base* is legal, because it does not account
/// for ambiguous conversions or conversions to private/protected
-/// bases.
-bool Sema::IsDerivedFrom(QualType Derived, QualType Base)
-{
+/// bases. This routine will use Paths to determine if there are
+/// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record
+/// information about all of the paths (if
+/// @c Paths.isRecordingPaths()).
+bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) {
+ bool FoundPath = false;
+
Derived = Context.getCanonicalType(Derived).getUnqualifiedType();
Base = Context.getCanonicalType(Base).getUnqualifiedType();
@@ -41,12 +78,120 @@
= static_cast<const CXXRecordDecl *>(DerivedType->getDecl());
for (CXXRecordDecl::base_class_const_iterator BaseSpec = Decl->bases_begin();
BaseSpec != Decl->bases_end(); ++BaseSpec) {
- if (Context.getCanonicalType(BaseSpec->getType()) == Base
- || IsDerivedFrom(BaseSpec->getType(), Base))
- return true;
+ // Find the record of the base class subobjects for this type.
+ QualType BaseType = Context.getCanonicalType(BaseSpec->getType());
+ BaseType = BaseType.getUnqualifiedType();
+
+ // Determine whether we need to visit this base class at all,
+ // updating the count of subobjects appropriately.
+ std::pair<bool, unsigned>& Subobjects = Paths.ClassSubobjects[BaseType];
+ bool VisitBase = true;
+ if (BaseSpec->isVirtual()) {
+ VisitBase = !Subobjects.first;
+ Subobjects.first = true;
+ } else
+ ++Subobjects.second;
+
+ if (Paths.isRecordingPaths()) {
+ // Add this base specifier to the current path.
+ BasePathElement Element;
+ Element.Base = &*BaseSpec;
+ if (BaseSpec->isVirtual())
+ Element.SubobjectNumber = 0;
+ else
+ Element.SubobjectNumber = Subobjects.second;
+ Paths.ScratchPath.push_back(Element);
+ }
+
+ if (Context.getCanonicalType(BaseSpec->getType()) == Base) {
+ // We've found the base we're looking for.
+ FoundPath = true;
+ if (Paths.isRecordingPaths()) {
+ // We have a path. Make a copy of it before moving on.
+ Paths.Paths.push_back(Paths.ScratchPath);
+ } else if (!Paths.isFindingAmbiguities()) {
+ // We found a path and we don't care about ambiguities;
+ // return immediately.
+ return FoundPath;
+ }
+ } else if (VisitBase && IsDerivedFrom(BaseSpec->getType(), Base, Paths)) {
+ // There is a path to the base we want. If we're not
+ // collecting paths or finding ambiguities, we're done.
+ FoundPath = true;
+ if (!Paths.isFindingAmbiguities())
+ return FoundPath;
+ }
+
+ // Pop this base specifier off the current path (if we're
+ // collecting paths).
+ if (Paths.isRecordingPaths())
+ Paths.ScratchPath.pop_back();
}
}
+ return FoundPath;
+}
+
+/// CheckDerivedToBaseConversion - Check whether the Derived-to-Base
+/// conversion (where Derived and Base are class types) is
+/// well-formed, meaning that the conversion is unambiguous (and
+/// FIXME: that all of the base classes are accessible). Returns true
+/// and emits a diagnostic if the code is ill-formed, returns false
+/// otherwise. Loc is the location where this routine should point to
+/// if there is an error, and Range is the source range to highlight
+/// if there is an error.
+bool
+Sema::CheckDerivedToBaseConversion(SourceLocation Loc, SourceRange Range,
+ QualType Derived, QualType Base) {
+ // First, determine whether the path from Derived to Base is
+ // ambiguous. This is slightly more expensive than checking whether
+ // the Derived to Base conversion exists, because here we need to
+ // explore multiple paths to determine if there is an ambiguity.
+ BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false);
+ bool DerivationOkay = IsDerivedFrom(Derived, Base, Paths);
+ assert(DerivationOkay && "Can only be used with a derived-to-base conversion");
+ if (!DerivationOkay)
+ return true;
+
+ if (Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) {
+ // We know that the derived-to-base conversion is
+ // ambiguous. Perform the derived-to-base search just one more
+ // time to compute all of the possible paths so that we can print
+ // them out. This is more expensive than any of the previous
+ // derived-to-base checks we've done, but at this point we know
+ // we'll be issuing a diagnostic so performance isn't as much of
+ // an issue.
+ Paths.clear();
+ Paths.setRecordingPaths(true);
+ bool StillOkay = IsDerivedFrom(Derived, Base, Paths);
+ assert(StillOkay && "Can only be used with a derived-to-base conversion");
+ if (!StillOkay)
+ return true;
+
+ // Build up a textual representation of the ambiguous paths, e.g.,
+ // D -> B -> A, that will be used to illustrate the ambiguous
+ // conversions in the diagnostic. We only print one of the paths
+ // to each base class subobject.
+ 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 ";
+ PathDisplayStr += Derived.getAsString();
+ for (BasePath::const_iterator Element = Path->begin();
+ Element != Path->end(); ++Element)
+ PathDisplayStr += " -> " + Element->Base->getType().getAsString();
+ }
+ }
+
+ Diag(Loc, diag::err_ambiguous_derived_to_base_conv,
+ Derived.getAsString(), Base.getAsString(), PathDisplayStr, Range);
+ return true;
+ }
+
return false;
}