diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-06-23 00:49:38 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-06-23 00:49:38 +0000 |
commit | 2b1ad8b42bbbe00a1845e566f52f1941b8dbc725 (patch) | |
tree | 3495e7b422dd60778531365ace56369101b6d0de /lib/Sema/SemaExprMember.cpp | |
parent | 647ba1bc2e5b9aeb3ed294353b0277f835effa7d (diff) |
Move all of Sema's member-access-related checking out of SemaExpr.cpp
and into a new file, SemaExprMember.cpp, bringing SemaExpr.cpp just
under 10,000 lines of code (ugh). No functionality change, although I
intend to do some refactoring of this code to address PR8368 at some
point in the "near" future.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133674 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaExprMember.cpp')
-rw-r--r-- | lib/Sema/SemaExprMember.cpp | 1582 |
1 files changed, 1582 insertions, 0 deletions
diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp new file mode 100644 index 0000000000..082691ffed --- /dev/null +++ b/lib/Sema/SemaExprMember.cpp @@ -0,0 +1,1582 @@ +//===--- SemaExprMember.cpp - Semantic Analysis for Expressions -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis member access expressions. +// +//===----------------------------------------------------------------------===// +#include "clang/Sema/SemaInternal.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Scope.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang; +using namespace sema; + +/// Determines if the given class is provably not derived from all of +/// the prospective base classes. +static bool IsProvablyNotDerivedFrom(Sema &SemaRef, + CXXRecordDecl *Record, + const llvm::SmallPtrSet<CXXRecordDecl*, 4> &Bases) { + if (Bases.count(Record->getCanonicalDecl())) + return false; + + RecordDecl *RD = Record->getDefinition(); + if (!RD) return false; + Record = cast<CXXRecordDecl>(RD); + + for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(), + E = Record->bases_end(); I != E; ++I) { + CanQualType BaseT = SemaRef.Context.getCanonicalType((*I).getType()); + CanQual<RecordType> BaseRT = BaseT->getAs<RecordType>(); + if (!BaseRT) return false; + + CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); + if (!IsProvablyNotDerivedFrom(SemaRef, BaseRecord, Bases)) + return false; + } + + return true; +} + +enum IMAKind { + /// The reference is definitely not an instance member access. + IMA_Static, + + /// The reference may be an implicit instance member access. + IMA_Mixed, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is not an instance method. + IMA_Mixed_StaticContext, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is from an unrelated class. + IMA_Mixed_Unrelated, + + /// The reference is definitely an implicit instance member access. + IMA_Instance, + + /// The reference may be to an unresolved using declaration. + IMA_Unresolved, + + /// The reference may be to an unresolved using declaration and the + /// context is not an instance method. + IMA_Unresolved_StaticContext, + + /// All possible referrents are instance members and the current + /// context is not an instance method. + IMA_Error_StaticContext, + + /// All possible referrents are instance members of an unrelated + /// class. + IMA_Error_Unrelated +}; + +/// The given lookup names class member(s) and is not being used for +/// an address-of-member expression. Classify the type of access +/// according to whether it's possible that this reference names an +/// instance member. This is best-effort; it is okay to +/// conservatively answer "yes", in which case some errors will simply +/// not be caught until template-instantiation. +static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, + Scope *CurScope, + const LookupResult &R) { + assert(!R.empty() && (*R.begin())->isCXXClassMember()); + + DeclContext *DC = SemaRef.getFunctionLevelDeclContext(); + + bool isStaticContext = + (!isa<CXXMethodDecl>(DC) || + cast<CXXMethodDecl>(DC)->isStatic()); + + // C++0x [expr.prim]p4: + // Otherwise, if a member-declarator declares a non-static data member + // of a class X, the expression this is a prvalue of type "pointer to X" + // within the optional brace-or-equal-initializer. + if (CurScope->getFlags() & Scope::ThisScope) + isStaticContext = false; + + if (R.isUnresolvableResult()) + return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved; + + // Collect all the declaring classes of instance members we find. + bool hasNonInstance = false; + bool hasField = false; + llvm::SmallPtrSet<CXXRecordDecl*, 4> Classes; + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + NamedDecl *D = *I; + + if (D->isCXXInstanceMember()) { + if (dyn_cast<FieldDecl>(D)) + hasField = true; + + CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext()); + Classes.insert(R->getCanonicalDecl()); + } + else + hasNonInstance = true; + } + + // If we didn't find any instance members, it can't be an implicit + // member reference. + if (Classes.empty()) + return IMA_Static; + + // If the current context is not an instance method, it can't be + // an implicit member reference. + if (isStaticContext) { + if (hasNonInstance) + return IMA_Mixed_StaticContext; + + if (SemaRef.getLangOptions().CPlusPlus0x && hasField) { + // C++0x [expr.prim.general]p10: + // An id-expression that denotes a non-static data member or non-static + // member function of a class can only be used: + // (...) + // - if that id-expression denotes a non-static data member and it + // appears in an unevaluated operand. + const Sema::ExpressionEvaluationContextRecord& record + = SemaRef.ExprEvalContexts.back(); + bool isUnevaluatedExpression = (record.Context == Sema::Unevaluated); + if (isUnevaluatedExpression) + return IMA_Mixed_StaticContext; + } + + return IMA_Error_StaticContext; + } + + CXXRecordDecl *contextClass; + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) + contextClass = MD->getParent()->getCanonicalDecl(); + else + contextClass = cast<CXXRecordDecl>(DC); + + // [class.mfct.non-static]p3: + // ...is used in the body of a non-static member function of class X, + // if name lookup (3.4.1) resolves the name in the id-expression to a + // non-static non-type member of some class C [...] + // ...if C is not X or a base class of X, the class member access expression + // is ill-formed. + if (R.getNamingClass() && + contextClass != R.getNamingClass()->getCanonicalDecl() && + contextClass->isProvablyNotDerivedFrom(R.getNamingClass())) + return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); + + // If we can prove that the current context is unrelated to all the + // declaring classes, it can't be an implicit member reference (in + // which case it's an error if any of those members are selected). + if (IsProvablyNotDerivedFrom(SemaRef, contextClass, Classes)) + return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); + + return (hasNonInstance ? IMA_Mixed : IMA_Instance); +} + +/// Diagnose a reference to a field with no object available. +static void DiagnoseInstanceReference(Sema &SemaRef, + const CXXScopeSpec &SS, + NamedDecl *rep, + const DeclarationNameInfo &nameInfo) { + SourceLocation Loc = nameInfo.getLoc(); + SourceRange Range(Loc); + if (SS.isSet()) Range.setBegin(SS.getRange().getBegin()); + + if (isa<FieldDecl>(rep) || isa<IndirectFieldDecl>(rep)) { + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(SemaRef.CurContext)) { + if (MD->isStatic()) { + // "invalid use of member 'x' in static member function" + SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) + << Range << nameInfo.getName(); + return; + } + } + + SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) + << nameInfo.getName() << Range; + return; + } + + SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; +} + +/// Builds an expression which might be an implicit member expression. +ExprResult +Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs) { + switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) { + case IMA_Instance: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, true); + + case IMA_Mixed: + case IMA_Mixed_Unrelated: + case IMA_Unresolved: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, false); + + case IMA_Static: + case IMA_Mixed_StaticContext: + case IMA_Unresolved_StaticContext: + if (TemplateArgs) + return BuildTemplateIdExpr(SS, R, false, *TemplateArgs); + return BuildDeclarationNameExpr(SS, R, false); + + case IMA_Error_StaticContext: + case IMA_Error_Unrelated: + DiagnoseInstanceReference(*this, SS, R.getRepresentativeDecl(), + R.getLookupNameInfo()); + return ExprError(); + } + + llvm_unreachable("unexpected instance member access kind"); + return ExprError(); +} + +/// Check an ext-vector component access expression. +/// +/// VK should be set in advance to the value kind of the base +/// expression. +static QualType +CheckExtVectorComponent(Sema &S, QualType baseType, ExprValueKind &VK, + SourceLocation OpLoc, const IdentifierInfo *CompName, + SourceLocation CompLoc) { + // FIXME: Share logic with ExtVectorElementExpr::containsDuplicateElements, + // see FIXME there. + // + // FIXME: This logic can be greatly simplified by splitting it along + // halving/not halving and reworking the component checking. + const ExtVectorType *vecType = baseType->getAs<ExtVectorType>(); + + // The vector accessor can't exceed the number of elements. + const char *compStr = CompName->getNameStart(); + + // This flag determines whether or not the component is one of the four + // special names that indicate a subset of exactly half the elements are + // to be selected. + bool HalvingSwizzle = false; + + // This flag determines whether or not CompName has an 's' char prefix, + // indicating that it is a string of hex values to be used as vector indices. + bool HexSwizzle = *compStr == 's' || *compStr == 'S'; + + bool HasRepeated = false; + bool HasIndex[16] = {}; + + int Idx; + + // Check that we've found one of the special components, or that the component + // names must come from the same set. + if (!strcmp(compStr, "hi") || !strcmp(compStr, "lo") || + !strcmp(compStr, "even") || !strcmp(compStr, "odd")) { + HalvingSwizzle = true; + } else if (!HexSwizzle && + (Idx = vecType->getPointAccessorIdx(*compStr)) != -1) { + do { + if (HasIndex[Idx]) HasRepeated = true; + HasIndex[Idx] = true; + compStr++; + } while (*compStr && (Idx = vecType->getPointAccessorIdx(*compStr)) != -1); + } else { + if (HexSwizzle) compStr++; + while ((Idx = vecType->getNumericAccessorIdx(*compStr)) != -1) { + if (HasIndex[Idx]) HasRepeated = true; + HasIndex[Idx] = true; + compStr++; + } + } + + if (!HalvingSwizzle && *compStr) { + // We didn't get to the end of the string. This means the component names + // didn't come from the same set *or* we encountered an illegal name. + S.Diag(OpLoc, diag::err_ext_vector_component_name_illegal) + << llvm::StringRef(compStr, 1) << SourceRange(CompLoc); + return QualType(); + } + + // Ensure no component accessor exceeds the width of the vector type it + // operates on. + if (!HalvingSwizzle) { + compStr = CompName->getNameStart(); + + if (HexSwizzle) + compStr++; + + while (*compStr) { + if (!vecType->isAccessorWithinNumElements(*compStr++)) { + S.Diag(OpLoc, diag::err_ext_vector_component_exceeds_length) + << baseType << SourceRange(CompLoc); + return QualType(); + } + } + } + + // The component accessor looks fine - now we need to compute the actual type. + // The vector type is implied by the component accessor. For example, + // vec4.b is a float, vec4.xy is a vec2, vec4.rgb is a vec3, etc. + // vec4.s0 is a float, vec4.s23 is a vec3, etc. + // vec4.hi, vec4.lo, vec4.e, and vec4.o all return vec2. + unsigned CompSize = HalvingSwizzle ? (vecType->getNumElements() + 1) / 2 + : CompName->getLength(); + if (HexSwizzle) + CompSize--; + + if (CompSize == 1) + return vecType->getElementType(); + + if (HasRepeated) VK = VK_RValue; + + QualType VT = S.Context.getExtVectorType(vecType->getElementType(), CompSize); + // Now look up the TypeDefDecl from the vector type. Without this, + // diagostics look bad. We want extended vector types to appear built-in. + for (unsigned i = 0, E = S.ExtVectorDecls.size(); i != E; ++i) { + if (S.ExtVectorDecls[i]->getUnderlyingType() == VT) + return S.Context.getTypedefType(S.ExtVectorDecls[i]); + } + return VT; // should never get here (a typedef type should always be found). +} + +static Decl *FindGetterSetterNameDeclFromProtocolList(const ObjCProtocolDecl*PDecl, + IdentifierInfo *Member, + const Selector &Sel, + ASTContext &Context) { + if (Member) + if (ObjCPropertyDecl *PD = PDecl->FindPropertyDeclaration(Member)) + return PD; + if (ObjCMethodDecl *OMD = PDecl->getInstanceMethod(Sel)) + return OMD; + + for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), + E = PDecl->protocol_end(); I != E; ++I) { + if (Decl *D = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, + Context)) + return D; + } + return 0; +} + +static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy, + IdentifierInfo *Member, + const Selector &Sel, + ASTContext &Context) { + // Check protocols on qualified interfaces. + Decl *GDecl = 0; + for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), + E = QIdTy->qual_end(); I != E; ++I) { + if (Member) + if (ObjCPropertyDecl *PD = (*I)->FindPropertyDeclaration(Member)) { + GDecl = PD; + break; + } + // Also must look for a getter or setter name which uses property syntax. + if (ObjCMethodDecl *OMD = (*I)->getInstanceMethod(Sel)) { + GDecl = OMD; + break; + } + } + if (!GDecl) { + for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), + E = QIdTy->qual_end(); I != E; ++I) { + // Search in the protocol-qualifier list of current protocol. + GDecl = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, + Context); + if (GDecl) + return GDecl; + } + } + return GDecl; +} + +ExprResult +Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, + bool IsArrow, SourceLocation OpLoc, + const CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs) { + // Even in dependent contexts, try to diagnose base expressions with + // obviously wrong types, e.g.: + // + // T* t; + // t.f; + // + // In Obj-C++, however, the above expression is valid, since it could be + // accessing the 'f' property if T is an Obj-C interface. The extra check + // allows this, while still reporting an error if T is a struct pointer. + if (!IsArrow) { + const PointerType *PT = BaseType->getAs<PointerType>(); + if (PT && (!getLangOptions().ObjC1 || + PT->getPointeeType()->isRecordType())) { + assert(BaseExpr && "cannot happen with implicit member accesses"); + Diag(NameInfo.getLoc(), diag::err_typecheck_member_reference_struct_union) + << BaseType << BaseExpr->getSourceRange(); + return ExprError(); + } + } + + assert(BaseType->isDependentType() || + NameInfo.getName().isDependentName() || + isDependentScopeSpecifier(SS)); + + // Get the type being accessed in BaseType. If this is an arrow, the BaseExpr + // must have pointer type, and the accessed type is the pointee. + return Owned(CXXDependentScopeMemberExpr::Create(Context, BaseExpr, BaseType, + IsArrow, OpLoc, + SS.getWithLocInContext(Context), + FirstQualifierInScope, + NameInfo, TemplateArgs)); +} + +/// We know that the given qualified member reference points only to +/// declarations which do not belong to the static type of the base +/// expression. Diagnose the problem. +static void DiagnoseQualifiedMemberReference(Sema &SemaRef, + Expr *BaseExpr, + QualType BaseType, + const CXXScopeSpec &SS, + NamedDecl *rep, + const DeclarationNameInfo &nameInfo) { + // If this is an implicit member access, use a different set of + // diagnostics. + if (!BaseExpr) + return DiagnoseInstanceReference(SemaRef, SS, rep, nameInfo); + + SemaRef.Diag(nameInfo.getLoc(), diag::err_qualified_member_of_unrelated) + << SS.getRange() << rep << BaseType; +} + +// Check whether the declarations we found through a nested-name +// specifier in a member expression are actually members of the base +// type. The restriction here is: +// +// C++ [expr.ref]p2: +// ... In these cases, the id-expression shall name a +// member of the class or of one of its base classes. +// +// So it's perfectly legitimate for the nested-name specifier to name +// an unrelated class, and for us to find an overload set including +// decls from classes which are not superclasses, as long as the decl +// we actually pick through overload resolution is from a superclass. +bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, + QualType BaseType, + const CXXScopeSpec &SS, + const LookupResult &R) { + const RecordType *BaseRT = BaseType->getAs<RecordType>(); + if (!BaseRT) { + // We can't check this yet because the base type is still + // dependent. + assert(BaseType->isDependentType()); + return false; + } + CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); + + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + // If this is an implicit member reference and we find a + // non-instance member, it's not an error. + if (!BaseExpr && !(*I)->isCXXInstanceMember()) + return false; + + // Note that we use the DC of the decl, not the underlying decl. + DeclContext *DC = (*I)->getDeclContext(); + while (DC->isTransparentContext()) + DC = DC->getParent(); + + if (!DC->isRecord()) + continue; + + llvm::SmallPtrSet<CXXRecordDecl*,4> MemberRecord; + MemberRecord.insert(cast<CXXRecordDecl>(DC)->getCanonicalDecl()); + + if (!IsProvablyNotDerivedFrom(*this, BaseRecord, MemberRecord)) + return false; + } + + DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, + R.getRepresentativeDecl(), + R.getLookupNameInfo()); + return true; +} + +static bool +LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, + SourceRange BaseRange, const RecordType *RTy, + SourceLocation OpLoc, CXXScopeSpec &SS, + bool HasTemplateArgs) { + RecordDecl *RDecl = RTy->getDecl(); + if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), + SemaRef.PDiag(diag::err_typecheck_incomplete_tag) + << BaseRange)) + return true; + + if (HasTemplateArgs) { + // LookupTemplateName doesn't expect these both to exist simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); + + bool MOUS; + SemaRef.LookupTemplateName(R, 0, SS, ObjectType, false, MOUS); + return false; + } + + DeclContext *DC = RDecl; + if (SS.isSet()) { + // If the member name was a qualified-id, look into the + // nested-name-specifier. + DC = SemaRef.computeDeclContext(SS, false); + + if (SemaRef.RequireCompleteDeclContext(SS, DC)) { + SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) + << SS.getRange() << DC; + return true; + } + + assert(DC && "Cannot handle non-computable dependent contexts in lookup"); + + if (!isa<TypeDecl>(DC)) { + SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) + << DC << SS.getRange(); + return true; + } + } + + // The record definition is complete, now look up the member. + SemaRef.LookupQualifiedName(R, DC); + + if (!R.empty()) + return false; + + // We didn't find anything with the given name, so try to correct + // for typos. + DeclarationName Name = R.getLookupName(); + if (SemaRef.CorrectTypo(R, 0, &SS, DC, false, Sema::CTC_MemberLookup) && + !R.empty() && + (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin()))) { + SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest) + << Name << DC << R.getLookupName() << SS.getRange() + << FixItHint::CreateReplacement(R.getNameLoc(), + R.getLookupName().getAsString()); + if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) + SemaRef.Diag(ND->getLocation(), diag::note_previous_decl) + << ND->getDeclName(); + return false; + } else { + R.clear(); + R.setLookupName(Name); + } + + return false; +} + +ExprResult +Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, + SourceLocation OpLoc, bool IsArrow, + CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs) { + if (BaseType->isDependentType() || + (SS.isSet() && isDependentScopeSpecifier(SS))) + return ActOnDependentMemberExpr(Base, BaseType, + IsArrow, OpLoc, + SS, FirstQualifierInScope, + NameInfo, TemplateArgs); + + LookupResult R(*this, NameInfo, LookupMemberName); + + // Implicit member accesses. + if (!Base) { + QualType RecordTy = BaseType; + if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType(); + if (LookupMemberExprInRecord(*this, R, SourceRange(), + RecordTy->getAs<RecordType>(), + OpLoc, SS, TemplateArgs != 0)) + return ExprError(); + + // Explicit member accesses. + } else { + ExprResult BaseResult = Owned(Base); + ExprResult Result = + LookupMemberExpr(R, BaseResult, IsArrow, OpLoc, + SS, /*ObjCImpDecl*/ 0, TemplateArgs != 0); + + if (BaseResult.isInvalid()) + return ExprError(); + Base = BaseResult.take(); + + if (Result.isInvalid()) { + Owned(Base); + return ExprError(); + } + + if (Result.get()) + return move(Result); + + // LookupMemberExpr can modify Base, and thus change BaseType + BaseType = Base->getType(); + } + + return BuildMemberReferenceExpr(Base, BaseType, + OpLoc, IsArrow, SS, FirstQualifierInScope, + R, TemplateArgs); +} + +static ExprResult +BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, + const CXXScopeSpec &SS, FieldDecl *Field, + DeclAccessPair FoundDecl, + const DeclarationNameInfo &MemberNameInfo); + +ExprResult +Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, + SourceLocation loc, + IndirectFieldDecl *indirectField, + Expr *baseObjectExpr, + SourceLocation opLoc) { + // First, build the expression that refers to the base object. + + bool baseObjectIsPointer = false; + Qualifiers baseQuals; + + // Case 1: the base of the indirect field is not a field. + VarDecl *baseVariable = indirectField->getVarDecl(); + CXXScopeSpec EmptySS; + if (baseVariable) { + assert(baseVariable->getType()->isRecordType()); + + // In principle we could have a member access expression that + // accesses an anonymous struct/union that's a static member of + // the base object's class. However, under the current standard, + // static data members cannot be anonymous structs or unions. + // Supporting this is as easy as building a MemberExpr here. + assert(!baseObjectExpr && "anonymous struct/union is static data member?"); + + DeclarationNameInfo baseNameInfo(DeclarationName(), loc); + + ExprResult result + = BuildDeclarationNameExpr(EmptySS, baseNameInfo, baseVariable); + if (result.isInvalid()) return ExprError(); + + baseObjectExpr = result.take(); + baseObjectIsPointer = false; + baseQuals = baseObjectExpr->getType().getQualifiers(); + + // Case 2: the base of the indirect field is a field and the user + // wrote a member expression. + } else if (baseObjectExpr) { + // The caller provided the base object expression. Determine + // whether its a pointer and whether it adds any qualifiers to the + // anonymous struct/union fields we're looking into. + QualType objectType = baseObjectExpr->getType(); + + if (const PointerType *ptr = objectType->getAs<PointerType>()) { + baseObjectIsPointer = true; + objectType = ptr->getPointeeType(); + } else { + baseObjectIsPointer = false; + } + baseQuals = objectType.getQualifiers(); + + // Case 3: the base of the indirect field is a field and we should + // build an implicit member access. + } else { + // We've found a member of an anonymous struct/union that is + // inside a non-anonymous struct/union, so in a well-formed + // program our base object expression is "this". + QualType ThisTy = getAndCaptureCurrentThisType(); + if (ThisTy.isNull()) { + Diag(loc, diag::err_invalid_member_use_in_static_method) + << indirectField->getDeclName(); + return ExprError(); + } + + // Our base object expression is "this". + baseObjectExpr + = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true); + baseObjectIsPointer = true; + baseQuals = ThisTy->castAs<PointerType>()->getPointeeType().getQualifiers(); + } + + // Build the implicit member references to the field of the + // anonymous struct/union. + Expr *result = baseObjectExpr; + IndirectFieldDecl::chain_iterator + FI = indirectField->chain_begin(), FEnd = indirectField->chain_end(); + + // Build the first member access in the chain with full information. + if (!baseVariable) { + FieldDecl *field = cast<FieldDecl>(*FI); + + // FIXME: use the real found-decl info! + DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); + + // Make a nameInfo that properly uses the anonymous name. + DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); + + result = BuildFieldReferenceExpr(*this, result, baseObjectIsPointer, + EmptySS, field, foundDecl, + memberNameInfo).take(); + baseObjectIsPointer = false; + + // FIXME: check qualified member access + } + + // In all cases, we should now skip the first declaration in the chain. + ++FI; + + while (FI != FEnd) { + FieldDecl *field = cast<FieldDecl>(*FI++); + + // FIXME: these are somewhat meaningless + DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); + DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); + + result = BuildFieldReferenceExpr(*this, result, /*isarrow*/ false, + (FI == FEnd? SS : EmptySS), field, + foundDecl, memberNameInfo).take(); + } + + return Owned(result); +} + +/// \brief Build a MemberExpr AST node. +static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, + const CXXScopeSpec &SS, ValueDecl *Member, + DeclAccessPair FoundDecl, + const DeclarationNameInfo &MemberNameInfo, + QualType Ty, + ExprValueKind VK, ExprObjectKind OK, + const TemplateArgumentListInfo *TemplateArgs = 0) { + return MemberExpr::Create(C, Base, isArrow, SS.getWithLocInContext(C), + Member, FoundDecl, MemberNameInfo, + TemplateArgs, Ty, VK, OK); +} + +ExprResult +Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, + SourceLocation OpLoc, bool IsArrow, + const CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, + bool SuppressQualifierCheck) { + QualType BaseType = BaseExprType; + if (IsArrow) { + assert(BaseType->isPointerType()); + BaseType = BaseType->getAs<PointerType>()->getPointeeType(); + } + R.setBaseObjectType(BaseType); + + const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo(); + DeclarationName MemberName = MemberNameInfo.getName(); + SourceLocation MemberLoc = MemberNameInfo.getLoc(); + + if (R.isAmbiguous()) + return ExprError(); + + if (R.empty()) { + // Rederive where we looked up. + DeclContext *DC = (SS.isSet() + ? computeDeclContext(SS, false) + : BaseType->getAs<RecordType>()->getDecl()); + + Diag(R.getNameLoc(), diag::err_no_member) + << MemberName << DC + << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); + return ExprError(); + } + + // Diagnose lookups that find only declarations from a non-base + // type. This is possible for either qualified lookups (which may + // have been qualified with an unrelated type) or implicit member + // expressions (which were found with unqualified lookup and thus + // may have come from an enclosing scope). Note that it's okay for + // lookup to find declarations from a non-base type as long as those + // aren't the ones picked by overload resolution. + if ((SS.isSet() || !BaseExpr || + (isa<CXXThisExpr>(BaseExpr) && + cast<CXXThisExpr>(BaseExpr)->isImplicit())) && + !SuppressQualifierCheck && + CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R)) + return ExprError(); + + // Construct an unresolved result if we in fact got an unresolved + // result. + if (R.isOverloadedResult() || R.isUnresolvableResult()) { + // Suppress any lookup-related diagnostics; we'll do these when we + // pick a member. + R.suppressDiagnostics(); + + UnresolvedMemberExpr *MemExpr + = UnresolvedMemberExpr::Create(Context, R.isUnresolvableResult(), + BaseExpr, BaseExprType, + IsArrow, OpLoc, + SS.getWithLocInContext(Context), + MemberNameInfo, + TemplateArgs, R.begin(), R.end()); + + return Owned(MemExpr); + } + + assert(R.isSingleResult()); + DeclAccessPair FoundDecl = R.begin().getPair(); + NamedDecl *MemberDecl = R.getFoundDecl(); + + // FIXME: diagnose the presence of template arguments now. + + // If the decl being referenced had an error, return an error for this + // sub-expr without emitting another error, in order to avoid cascading + // error cases. + if (MemberDecl->isInvalidDecl()) + return ExprError(); + + // Handle the implicit-member-access case. + if (!BaseExpr) { + // If this is not an instance member, convert to a non-member access. + if (!MemberDecl->isCXXInstanceMember()) + return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl); + + SourceLocation Loc = R.getNameLoc(); + if (SS.getRange().isValid()) + Loc = SS.getRange().getBegin(); + BaseExpr = new (Context) CXXThisExpr(Loc, BaseExprType,/*isImplicit=*/true); + } + + bool ShouldCheckUse = true; + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MemberDecl)) { + // Don't diagnose the use of a virtual member function unless it's + // explicitly qualified. + if (MD->isVirtual() && !SS.isSet()) + ShouldCheckUse = false; + } + + // Check the use of this member. + if (ShouldCheckUse && DiagnoseUseOfDecl(MemberDecl, MemberLoc)) { + Owned(BaseExpr); + return ExprError(); + } + + // Perform a property load on the base regardless of whether we + // actually need it for the declaration. + if (BaseExpr->getObjectKind() == OK_ObjCProperty) { + ExprResult Result = ConvertPropertyForRValue(BaseExpr); + if (Result.isInvalid()) + return ExprError(); + BaseExpr = Result.take(); + } + + if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) + return BuildFieldReferenceExpr(*this, BaseExpr, IsArrow, + SS, FD, FoundDecl, MemberNameInfo); + + if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl)) + // We may have found a field within an anonymous union or struct + // (C++ [class.union]). + return BuildAnonymousStructUnionMemberReference(SS, MemberLoc, FD, + BaseExpr, OpLoc); + + if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) { + MarkDeclarationReferenced(MemberLoc, Var); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + Var, FoundDecl, MemberNameInfo, + Var->getType().getNonReferenceType(), + VK_LValue, OK_Ordinary)); + } + + if (CXXMethodDecl *MemberFn = dyn_cast<CXXMethodDecl>(MemberDecl)) { + ExprValueKind valueKind; + QualType type; + if (MemberFn->isInstance()) { + valueKind = VK_RValue; + type = Context.BoundMemberTy; + } else { + valueKind = VK_LValue; + type = MemberFn->getType(); + } + + MarkDeclarationReferenced(MemberLoc, MemberDecl); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + MemberFn, FoundDecl, MemberNameInfo, + type, valueKind, OK_Ordinary)); + } + assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?"); + + if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) { + MarkDeclarationReferenced(MemberLoc, MemberDecl); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + Enum, FoundDecl, MemberNameInfo, + Enum->getType(), VK_RValue, OK_Ordinary)); + } + + Owned(BaseExpr); + + // We found something that we didn't expect. Complain. + if (isa<TypeDecl>(MemberDecl)) + Diag(MemberLoc, diag::err_typecheck_member_reference_type) + << MemberName << BaseType << int(IsArrow); + else + Diag(MemberLoc, diag::err_typecheck_member_reference_unknown) + << MemberName << BaseType << int(IsArrow); + + Diag(MemberDecl->getLocation(), diag::note_member_declared_here) + << MemberName; + R.suppressDiagnostics(); + return ExprError(); |