Implement disambiguation of base class members via a

nested-name-specifier. For example, this allows member access in
diamond-shaped hierarchies like:

  struct Base {
    void Foo();
    int Member;
  };

  struct D1 : public Base {};
  struct D2 : public Base {};

  struct Derived : public D1, public D2 { }

  void Test(Derived d) {
    d.Member = 17; // error: ambiguous cast from Derived to Base
    d.D1::Member = 17; // error: okay, modify D1's Base's Member
  }

Fixes PR5820 and <rdar://problem/7535045>. Also, eliminate some
redundancy between Sema::PerformObjectMemberConversion() and
Sema::PerformObjectArgumentInitialization() -- the latter now calls
the former.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@97674 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Douglas Gregor 2010-03-03 22:53:40 +00:00
Родитель 9f9efe6d58
Коммит b29b37d7e5
7 изменённых файлов: 201 добавлений и 46 удалений

Просмотреть файл

@ -1097,12 +1097,16 @@ public:
ImplicitConversionSequence
TryObjectArgumentInitialization(QualType FromType, CXXMethodDecl *Method,
CXXRecordDecl *ActingContext);
bool PerformObjectArgumentInitialization(Expr *&From, CXXMethodDecl *Method);
bool PerformObjectArgumentInitialization(Expr *&From,
NestedNameSpecifier *Qualifier,
CXXMethodDecl *Method);
ImplicitConversionSequence TryContextuallyConvertToBool(Expr *From);
bool PerformContextuallyConvertToBool(Expr *&From);
bool PerformObjectMemberConversion(Expr *&From, NamedDecl *Member);
bool PerformObjectMemberConversion(Expr *&From,
NestedNameSpecifier *Qualifier,
NamedDecl *Member);
// Members have to be NamespaceDecl* or TranslationUnitDecl*.
// TODO: make this is a typesafe union.

Просмотреть файл

@ -635,7 +635,7 @@ Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
MemberType = Context.getQualifiedType(MemberType, NewQuals);
MarkDeclarationReferenced(Loc, *FI);
PerformObjectMemberConversion(Result, *FI);
PerformObjectMemberConversion(Result, /*FIXME:Qualifier=*/0, *FI);
// FIXME: Might this end up being a qualified name?
Result = new (Context) MemberExpr(Result, BaseObjectIsPointer, *FI,
OpLoc, MemberType);
@ -1357,29 +1357,111 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
/// \brief Cast member's object to its own class if necessary.
bool
Sema::PerformObjectMemberConversion(Expr *&From, NamedDecl *Member) {
if (FieldDecl *FD = dyn_cast<FieldDecl>(Member))
if (CXXRecordDecl *RD =
dyn_cast<CXXRecordDecl>(FD->getDeclContext())) {
QualType DestType =
Context.getCanonicalType(Context.getTypeDeclType(RD));
if (DestType->isDependentType() || From->getType()->isDependentType())
return false;
QualType FromRecordType = From->getType();
QualType DestRecordType = DestType;
if (FromRecordType->getAs<PointerType>()) {
DestType = Context.getPointerType(DestType);
FromRecordType = FromRecordType->getPointeeType();
}
if (!Context.hasSameUnqualifiedType(FromRecordType, DestRecordType) &&
CheckDerivedToBaseConversion(FromRecordType,
DestRecordType,
From->getSourceRange().getBegin(),
From->getSourceRange()))
return true;
ImpCastExprToType(From, DestType, CastExpr::CK_DerivedToBase,
/*isLvalue=*/true);
Sema::PerformObjectMemberConversion(Expr *&From,
NestedNameSpecifier *Qualifier,
NamedDecl *Member) {
CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Member->getDeclContext());
if (!RD)
return false;
QualType DestRecordType;
QualType DestType;
QualType FromRecordType;
QualType FromType = From->getType();
bool PointerConversions = false;
if (isa<FieldDecl>(Member)) {
DestRecordType = Context.getCanonicalType(Context.getTypeDeclType(RD));
if (FromType->getAs<PointerType>()) {
DestType = Context.getPointerType(DestRecordType);
FromRecordType = FromType->getPointeeType();
PointerConversions = true;
} else {
DestType = DestRecordType;
FromRecordType = FromType;
}
} else if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member)) {
if (Method->isStatic())
return false;
DestType = Method->getThisType(Context);
DestRecordType = DestType->getPointeeType();
if (FromType->getAs<PointerType>()) {
FromRecordType = FromType->getPointeeType();
PointerConversions = true;
} else {
FromRecordType = FromType;
DestType = DestRecordType;
}
} else {
// No conversion necessary.
return false;
}
if (DestType->isDependentType() || FromType->isDependentType())
return false;
// If the unqualified types are the same, no conversion is necessary.
if (Context.hasSameUnqualifiedType(FromRecordType, DestRecordType))
return false;
// C++ [class.member.lookup]p8:
// [...] Ambiguities can often be resolved by qualifying a name with its
// class name.
//
// If the member was a qualified name and the qualified referred to a
// specific base subobject type, we'll cast to that intermediate type
// first and then to the object in which the member is declared. That allows
// one to resolve ambiguities in, e.g., a diamond-shaped hierarchy such as:
//
// class Base { public: int x; };
// class Derived1 : public Base { };
// class Derived2 : public Base { };
// class VeryDerived : public Derived1, public Derived2 { void f(); };
//
// void VeryDerived::f() {
// x = 17; // error: ambiguous base subobjects
// Derived1::x = 17; // okay, pick the Base subobject of Derived1
// }
QualType IntermediateRecordType;
QualType IntermediateType;
if (Qualifier) {
if (const RecordType *IntermediateRecord
= Qualifier->getAsType()->getAs<RecordType>()) {
IntermediateRecordType = QualType(IntermediateRecord, 0);
IntermediateType = IntermediateRecordType;
if (PointerConversions)
IntermediateType = Context.getPointerType(IntermediateType);
}
}
if (!IntermediateType.isNull() &&
IsDerivedFrom(FromRecordType, IntermediateRecordType) &&
IsDerivedFrom(IntermediateRecordType, DestRecordType)) {
if (CheckDerivedToBaseConversion(FromRecordType, IntermediateRecordType,
From->getSourceRange().getBegin(),
From->getSourceRange()) ||
CheckDerivedToBaseConversion(IntermediateRecordType, DestRecordType,
From->getSourceRange().getBegin(),
From->getSourceRange()))
return true;
ImpCastExprToType(From, IntermediateType, CastExpr::CK_DerivedToBase,
/*isLvalue=*/!PointerConversions);
ImpCastExprToType(From, DestType, CastExpr::CK_DerivedToBase,
/*isLvalue=*/!PointerConversions);
return false;
}
if (CheckDerivedToBaseConversion(FromRecordType,
DestRecordType,
From->getSourceRange().getBegin(),
From->getSourceRange()))
return true;
ImpCastExprToType(From, DestType, CastExpr::CK_DerivedToBase,
/*isLvalue=*/true);
return false;
}
@ -2666,7 +2748,7 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType,
}
MarkDeclarationReferenced(MemberLoc, FD);
if (PerformObjectMemberConversion(BaseExpr, FD))
if (PerformObjectMemberConversion(BaseExpr, Qualifier, FD))
return ExprError();
return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS,
FD, MemberLoc, MemberType));
@ -6651,7 +6733,7 @@ Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S,
Res = BuildAnonymousStructUnionMemberReference(
OC.LocEnd, MemberDecl, Res, OC.LocEnd).takeAs<Expr>();
} else {
PerformObjectMemberConversion(Res, MemberDecl);
PerformObjectMemberConversion(Res, /*Qualifier=*/0, MemberDecl);
// MemberDecl->getType() doesn't get the right qualifiers, but it
// doesn't matter here.
Res = new (Context) MemberExpr(Res, false, MemberDecl, OC.LocEnd,

Просмотреть файл

@ -2881,7 +2881,7 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base,
CXXMemberCallExpr *Sema::BuildCXXMemberCallExpr(Expr *Exp,
CXXMethodDecl *Method) {
if (PerformObjectArgumentInitialization(Exp, Method))
if (PerformObjectArgumentInitialization(Exp, /*Qualifier=*/0, Method))
assert(0 && "Calling BuildCXXMemberCallExpr with invalid call?");
MemberExpr *ME =

Просмотреть файл

@ -3365,7 +3365,8 @@ InitializationSequence::Perform(Sema &S,
// FIXME: Should we move this initialization into a separate
// derived-to-base conversion? I believe the answer is "no", because
// we don't want to turn off access control here for c-style casts.
if (S.PerformObjectArgumentInitialization(CurInitExpr, Conversion))
if (S.PerformObjectArgumentInitialization(CurInitExpr, /*Qualifier=*/0,
Conversion))
return S.ExprError();
// Do a little dance to make sure that CurInit has the proper

Просмотреть файл

@ -2304,7 +2304,9 @@ Sema::TryObjectArgumentInitialization(QualType OrigFromType,
/// the implicit object parameter for the given Method with the given
/// expression.
bool
Sema::PerformObjectArgumentInitialization(Expr *&From, CXXMethodDecl *Method) {
Sema::PerformObjectArgumentInitialization(Expr *&From,
NestedNameSpecifier *Qualifier,
CXXMethodDecl *Method) {
QualType FromRecordType, DestType;
QualType ImplicitParamRecordType =
Method->getThisType(Context)->getAs<PointerType>()->getPointeeType();
@ -2327,15 +2329,12 @@ Sema::PerformObjectArgumentInitialization(Expr *&From, CXXMethodDecl *Method) {
diag::err_implicit_object_parameter_init)
<< ImplicitParamRecordType << FromRecordType << From->getSourceRange();
if (ICS.Standard.Second == ICK_Derived_To_Base &&
CheckDerivedToBaseConversion(FromRecordType,
ImplicitParamRecordType,
From->getSourceRange().getBegin(),
From->getSourceRange()))
return true;
if (ICS.Standard.Second == ICK_Derived_To_Base)
return PerformObjectMemberConversion(From, Qualifier, Method);
ImpCastExprToType(From, DestType, CastExpr::CK_DerivedToBase,
/*isLvalue=*/true);
if (!Context.hasSameType(From->getType(), DestType))
ImpCastExprToType(From, DestType, CastExpr::CK_NoOp,
/*isLvalue=*/!From->getType()->getAs<PointerType>());
return false;
}
@ -5545,7 +5544,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn,
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
CheckMemberOperatorAccess(OpLoc, Args[0], Method, Best->getAccess());
if (PerformObjectArgumentInitialization(Input, Method))
if (PerformObjectArgumentInitialization(Input, /*Qualifier=*/0, Method))
return ExprError();
} else {
// Convert the arguments.
@ -5738,7 +5737,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
if (Arg1.isInvalid())
return ExprError();
if (PerformObjectArgumentInitialization(Args[0], Method))
if (PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/0,
Method))
return ExprError();
Args[1] = RHS = Arg1.takeAs<Expr>();
@ -5904,7 +5904,8 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
// Convert the arguments.
CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
if (PerformObjectArgumentInitialization(Args[0], Method))
if (PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/0,
Method))
return ExprError();
// Convert the arguments.
@ -6009,12 +6010,15 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
MemberExpr *MemExpr;
CXXMethodDecl *Method = 0;
NestedNameSpecifier *Qualifier = 0;
if (isa<MemberExpr>(NakedMemExpr)) {
MemExpr = cast<MemberExpr>(NakedMemExpr);
Method = cast<CXXMethodDecl>(MemExpr->getMemberDecl());
Qualifier = MemExpr->getQualifier();
} else {
UnresolvedMemberExpr *UnresExpr = cast<UnresolvedMemberExpr>(NakedMemExpr);
Qualifier = UnresExpr->getQualifier();
QualType ObjectType = UnresExpr->getBaseType();
// Add overload candidates
@ -6113,7 +6117,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
// Convert the object argument (for a non-static member function call).
Expr *ObjectArg = MemExpr->getBase();
if (!Method->isStatic() &&
PerformObjectArgumentInitialization(ObjectArg, Method))
PerformObjectArgumentInitialization(ObjectArg, Qualifier, Method))
return ExprError();
MemExpr->setBase(ObjectArg);
@ -6333,7 +6337,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object,
bool IsError = false;
// Initialize the implicit object parameter.
IsError |= PerformObjectArgumentInitialization(Object, Method);
IsError |= PerformObjectArgumentInitialization(Object, /*Qualifier=*/0,
Method);
TheCall->setArg(0, Object);
@ -6458,7 +6463,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, ExprArg BaseIn, SourceLocation OpLoc) {
// Convert the object parameter.
CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
if (PerformObjectArgumentInitialization(Base, Method))
if (PerformObjectArgumentInitialization(Base, /*Qualifier=*/0, Method))
return ExprError();
// No concerns about early exits now.

Просмотреть файл

@ -969,7 +969,7 @@ public:
assert(!Qualifier && "Can't have an unnamed field with a qualifier!");
Expr *BaseExpr = Base.takeAs<Expr>();
if (getSema().PerformObjectMemberConversion(BaseExpr, Member))
if (getSema().PerformObjectMemberConversion(BaseExpr, Qualifier, Member))
return getSema().ExprError();
MemberExpr *ME =

Просмотреть файл

@ -0,0 +1,63 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// FIXME: Access control checks
namespace PR5820 {
// also <rdar://problem/7535045>
struct Base {
void Foo();
int Member;
};
struct D1 : public Base {};
struct D2 : public Base {};
struct Derived : public D1, public D2 {
void Inner();
};
void Test() {
Derived d;
d.D1::Foo();
d.D1::Member = 17;
}
void Derived::Inner() {
D1::Foo();
D1::Member = 42;
this->D1::Foo();
this->D1::Member = 42;
}
}
template<typename T>
struct BaseT {
void Foo(); // expected-note{{found by ambiguous name lookup}}
int Member;
};
template<typename T> struct Derived1T : BaseT<T> { };
template<typename T> struct Derived2T : BaseT<T> { };
template<typename T>
struct DerivedT : public Derived1T<T>, public Derived2T<T> {
void Inner();
};
template<typename T>
void DerivedT<T>::Inner() {
Derived1T<T>::Foo();
Derived2T<T>::Member = 42;
this->Derived1T<T>::Foo();
this->Derived2T<T>::Member = 42;
this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT<int>'}}
}
template<typename T>
void Test(DerivedT<T> d) {
d.template Derived1T<T>::Foo();
d.template Derived2T<T>::Member = 17;
d.Inner(); // expected-note{{in instantiation}}
}
template void Test(DerivedT<int>);