llvm-project/clang/lib/Sema/SemaExprMember.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1883 lines
73 KiB
C++
Raw Normal View History

//===--- SemaExprMember.cpp - Semantic Analysis for Expressions -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements semantic analysis member access expressions.
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/Overload.h"
This patch implements capturing of variables within generic lambdas. Both Richard and I felt that the current wording in the working paper needed some tweaking - Please see http://llvm-reviews.chandlerc.com/D2035 for additional context and references to core-reflector messages that discuss wording tweaks. What is implemented is what we had intended to specify in Bristol; but, recently felt that the specification might benefit from some tweaking and fleshing. As a rough attempt to explain the semantics: If a nested lambda with a default-capture names a variable within its body, and if the enclosing full expression that contains the name of that variable is instantiation-dependent - then an enclosing lambda that is capture-ready (i.e. within a non-dependent context) must capture that variable, if all intervening nested lambdas can potentially capture that variable if they need to, and all intervening parent lambdas of the capture-ready lambda can and do capture the variable. Of note, 'this' capturing is also currently underspecified in the working paper for generic lambdas. What is implemented here is if the set of candidate functions in a nested generic lambda includes both static and non-static member functions (regardless of viability checking - i.e. num and type of parameters/arguments) - and if all intervening nested-inner lambdas between the capture-ready lambda and the function-call containing nested lambda can capture 'this' and if all enclosing lambdas of the capture-ready lambda can capture 'this', then 'this' is speculatively captured by that capture-ready lambda. Hopefully a paper for the C++ committee (that Richard and I had started some preliminary work on) is forthcoming. This essentially makes generic lambdas feature complete, except for known bugs. The more prominent ones (and the ones I am currently aware of) being: - generic lambdas and init-captures are broken - but a patch that fixes this is already in the works ... - nested variadic expansions such as: auto K = [](auto ... OuterArgs) { vp([=](auto ... Is) { decltype(OuterArgs) OA = OuterArgs; return 0; }(5)...); return 0; }; auto M = K('a', ' ', 1, " -- ", 3.14); currently cause crashes. I think I know how to fix this (since I had done so in my initial implementation) - but it will probably take some work and back & forth with Doug and Richard. A warm thanks to all who provided feedback - and especially to Doug Gregor and Richard Smith for their pivotal guidance: their insight and prestidigitation in such matters is boundless! Now let's hope this commit doesn't upset the buildbot gods ;) Thanks! llvm-svn: 194188
2013-11-07 13:17:06 +08:00
#include "clang/AST/ASTLambda.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"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
using namespace clang;
using namespace sema;
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> BaseSet;
/// Determines if the given class is provably not derived from all of
/// the prospective base classes.
static bool isProvablyNotDerivedFrom(Sema &SemaRef, CXXRecordDecl *Record,
const BaseSet &Bases) {
auto BaseIsNotInSet = [&Bases](const CXXRecordDecl *Base) {
return !Bases.count(Base->getCanonicalDecl());
};
return BaseIsNotInSet(Record) && Record->forallBases(BaseIsNotInSet);
}
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 might be 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 is a contextually-permitted abstract member reference.
IMA_Abstract,
/// The reference may be to an unresolved using declaration and the
/// context is not an instance method.
IMA_Unresolved_StaticContext,
// The reference refers to a field which is not a member of the containing
// class, which is allowed because we're in C++11 mode and the context is
// unevaluated.
IMA_Field_Uneval_Context,
/// 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 in dependent contexts; 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,
const LookupResult &R) {
assert(!R.empty() && (*R.begin())->isCXXClassMember());
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
bool isStaticContext = SemaRef.CXXThisTypeOverride.isNull() &&
(!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic());
if (R.isUnresolvableResult())
return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved;
// Collect all the declaring classes of instance members we find.
bool hasNonInstance = false;
bool isField = false;
BaseSet Classes;
for (NamedDecl *D : R) {
// Look through any using decls.
D = D->getUnderlyingDecl();
if (D->isCXXInstanceMember()) {
isField |= isa<FieldDecl>(D) || isa<MSPropertyDecl>(D) ||
isa<IndirectFieldDecl>(D);
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;
// C++11 [expr.prim.general]p12:
// 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.
//
// This rule is specific to C++11. However, we also permit this form
// in unevaluated inline assembly operands, like the operand to a SIZE.
IMAKind AbstractInstanceResult = IMA_Static; // happens to be 'false'
assert(!AbstractInstanceResult);
switch (SemaRef.ExprEvalContexts.back().Context) {
case Sema::ExpressionEvaluationContext::Unevaluated:
case Sema::ExpressionEvaluationContext::UnevaluatedList:
if (isField && SemaRef.getLangOpts().CPlusPlus11)
AbstractInstanceResult = IMA_Field_Uneval_Context;
break;
case Sema::ExpressionEvaluationContext::UnevaluatedAbstract:
AbstractInstanceResult = IMA_Abstract;
break;
case Sema::ExpressionEvaluationContext::DiscardedStatement:
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
break;
}
// 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;
return AbstractInstanceResult ? AbstractInstanceResult
: 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->getCanonicalDecl() !=
R.getNamingClass()->getCanonicalDecl()) {
// If the naming class is not the current context, this was a qualified
// member name lookup, and it's sufficient to check that we have the naming
// class as a base class.
Classes.clear();
Classes.insert(R.getNamingClass()->getCanonicalDecl());
}
// 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 :
AbstractInstanceResult ? AbstractInstanceResult :
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());
// Look through using shadow decls and aliases.
Rep = Rep->getUnderlyingDecl();
DeclContext *FunctionLevelDC = SemaRef.getFunctionLevelDeclContext();
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FunctionLevelDC);
CXXRecordDecl *ContextClass = Method ? Method->getParent() : nullptr;
CXXRecordDecl *RepClass = dyn_cast<CXXRecordDecl>(Rep->getDeclContext());
bool InStaticMethod = Method && Method->isStatic();
bool IsField = isa<FieldDecl>(Rep) || isa<IndirectFieldDecl>(Rep);
if (IsField && InStaticMethod)
// "invalid use of member 'x' in static member function"
SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method)
<< Range << nameInfo.getName();
else if (ContextClass && RepClass && SS.isEmpty() && !InStaticMethod &&
!RepClass->Equals(ContextClass) && RepClass->Encloses(ContextClass))
// Unqualified lookup in a non-static member function found a member of an
// enclosing class.
SemaRef.Diag(Loc, diag::err_nested_non_static_member_use)
<< IsField << RepClass << nameInfo.getName() << ContextClass << Range;
else if (IsField)
SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use)
<< nameInfo.getName() << Range;
else
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, SourceLocation TemplateKWLoc, LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
UnresolvedLookupExpr *AsULE) {
switch (ClassifyImplicitMemberAccess(*this, R)) {
case IMA_Instance:
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, true, S);
case IMA_Mixed:
case IMA_Mixed_Unrelated:
case IMA_Unresolved:
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, false,
S);
case IMA_Field_Uneval_Context:
Diag(R.getNameLoc(), diag::warn_cxx98_compat_non_static_member_use)
<< R.getLookupNameInfo().getName();
LLVM_FALLTHROUGH;
case IMA_Static:
case IMA_Abstract:
case IMA_Mixed_StaticContext:
case IMA_Unresolved_StaticContext:
if (TemplateArgs || TemplateKWLoc.isValid())
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, false, TemplateArgs);
return AsULE ? AsULE : 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");
}
/// Determine whether input char is from rgba component set.
static bool
IsRGBA(char c) {
switch (c) {
case 'r':
case 'g':
case 'b':
case 'a':
return true;
default:
return false;
}
}
// OpenCL v1.1, s6.1.7
// The component swizzle length must be in accordance with the acceptable
// vector sizes.
static bool IsValidOpenCLComponentSwizzleLength(unsigned len)
{
return (len >= 1 && len <= 4) || len == 8 || len == 16;
}
/// 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') && compStr[1];
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) {
bool HasRGBA = IsRGBA(*compStr);
do {
// Ensure that xyzw and rgba components don't intermingle.
if (HasRGBA != IsRGBA(*compStr))
break;
if (HasIndex[Idx]) HasRepeated = true;
HasIndex[Idx] = true;
compStr++;
} while (*compStr && (Idx = vecType->getPointAccessorIdx(*compStr)) != -1);
// Emit a warning if an rgba selector is used earlier than OpenCL 2.2
if (HasRGBA || (*compStr && IsRGBA(*compStr))) {
if (S.getLangOpts().OpenCL && S.getLangOpts().OpenCLVersion < 220) {
const char *DiagBegin = HasRGBA ? CompName->getNameStart() : compStr;
S.Diag(OpLoc, diag::ext_opencl_ext_vector_type_rgba_selector)
<< StringRef(DiagBegin, 1)
<< S.getLangOpts().OpenCLVersion << SourceRange(CompLoc);
}
}
} 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)
<< 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++, HexSwizzle)) {
S.Diag(OpLoc, diag::err_ext_vector_component_exceeds_length)
<< baseType << SourceRange(CompLoc);
return QualType();
}
}
}
// OpenCL mode requires swizzle length to be in accordance with accepted
// sizes. Clang however supports arbitrary lengths for other languages.
if (S.getLangOpts().OpenCL && !HalvingSwizzle) {
unsigned SwizzleLength = CompName->getLength();
if (HexSwizzle)
SwizzleLength--;
if (IsValidOpenCLComponentSwizzleLength(SwizzleLength) == false) {
S.Diag(OpLoc, diag::err_opencl_ext_vector_component_invalid_length)
<< SwizzleLength << 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 (Sema::ExtVectorDeclsType::iterator
I = S.ExtVectorDecls.begin(S.getExternalSource()),
E = S.ExtVectorDecls.end();
I != E; ++I) {
if ((*I)->getUnderlyingType() == VT)
return S.Context.getTypedefType(*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, ObjCPropertyQueryKind::OBJC_PR_query_instance))
return PD;
if (ObjCMethodDecl *OMD = PDecl->getInstanceMethod(Sel))
return OMD;
for (const auto *I : PDecl->protocols()) {
if (Decl *D = FindGetterSetterNameDeclFromProtocolList(I, Member, Sel,
Context))
return D;
}
return nullptr;
}
static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy,
IdentifierInfo *Member,
const Selector &Sel,
ASTContext &Context) {
// Check protocols on qualified interfaces.
Decl *GDecl = nullptr;
for (const auto *I : QIdTy->quals()) {
if (Member)
if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration(
Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) {
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 (const auto *I : QIdTy->quals()) {
// 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,
SourceLocation TemplateKWLoc,
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 && (!getLangOpts().ObjC ||
PT->getPointeeType()->isRecordType())) {
assert(BaseExpr && "cannot happen with implicit member accesses");
Diag(OpLoc, diag::err_typecheck_member_reference_struct_union)
<< BaseType << BaseExpr->getSourceRange() << NameInfo.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 CXXDependentScopeMemberExpr::Create(
Context, BaseExpr, BaseType, IsArrow, OpLoc,
SS.getWithLocInContext(Context), TemplateKWLoc, 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) {
CXXRecordDecl *BaseRecord =
cast_or_null<CXXRecordDecl>(computeDeclContext(BaseType));
if (!BaseRecord) {
// We can't check this yet because the base type is still
// dependent.
assert(BaseType->isDependentType());
return false;
}
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;
CXXRecordDecl *MemberRecord = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
if (BaseRecord->getCanonicalDecl() == MemberRecord ||
!BaseRecord->isProvablyNotDerivedFrom(MemberRecord))
return false;
}
DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS,
R.getRepresentativeDecl(),
R.getLookupNameInfo());
return true;
}
namespace {
// Callback to only accept typo corrections that are either a ValueDecl or a
// FunctionTemplateDecl and are declared in the current record or, for a C++
// classes, one of its base classes.
class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback {
public:
explicit RecordMemberExprValidatorCCC(const RecordType *RTy)
: Record(RTy->getDecl()) {
// Don't add bare keywords to the consumer since they will always fail
// validation by virtue of not being associated with any decls.
WantTypeSpecifiers = false;
WantExpressionKeywords = false;
WantCXXNamedCasts = false;
WantFunctionLikeCasts = false;
WantRemainingKeywords = false;
}
bool ValidateCandidate(const TypoCorrection &candidate) override {
NamedDecl *ND = candidate.getCorrectionDecl();
// Don't accept candidates that cannot be member functions, constants,
// variables, or templates.
if (!ND || !(isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)))
return false;
// Accept candidates that occur in the current record.
if (Record->containsDecl(ND))
return true;
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Record)) {
// Accept candidates that occur in any of the current class' base classes.
for (const auto &BS : RD->bases()) {
if (const RecordType *BSTy =
dyn_cast_or_null<RecordType>(BS.getType().getTypePtrOrNull())) {
if (BSTy->getDecl()->containsDecl(ND))
return true;
}
}
}
return false;
}
std::unique_ptr<CorrectionCandidateCallback> clone() override {
return std::make_unique<RecordMemberExprValidatorCCC>(*this);
}
private:
const RecordDecl *const Record;
};
}
static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
Expr *BaseExpr,
const RecordType *RTy,
SourceLocation OpLoc, bool IsArrow,
CXXScopeSpec &SS, bool HasTemplateArgs,
SourceLocation TemplateKWLoc,
TypoExpr *&TE) {
SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange();
RecordDecl *RDecl = RTy->getDecl();
if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) &&
SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0),
diag::err_typecheck_incomplete_tag,
BaseRange))
return true;
if (HasTemplateArgs || TemplateKWLoc.isValid()) {
// LookupTemplateName doesn't expect these both to exist simultaneously.
QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0);
bool MOUS;
return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS,
TemplateKWLoc);
}
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, SS);
if (!R.empty())
return false;
DeclarationName Typo = R.getLookupName();
SourceLocation TypoLoc = R.getNameLoc();
struct QueryState {
Sema &SemaRef;
DeclarationNameInfo NameInfo;
Sema::LookupNameKind LookupKind;
Sema::RedeclarationKind Redecl;
};
QueryState Q = {R.getSema(), R.getLookupNameInfo(), R.getLookupKind(),
R.redeclarationKind()};
RecordMemberExprValidatorCCC CCC(RTy);
TE = SemaRef.CorrectTypoDelayed(
R.getLookupNameInfo(), R.getLookupKind(), nullptr, &SS, CCC,
[=, &SemaRef](const TypoCorrection &TC) {
if (TC) {
assert(!TC.isKeyword() &&
"Got a keyword as a correction for a member!");
bool DroppedSpecifier =
TC.WillReplaceSpecifier() &&
Typo.getAsString() == TC.getAsString(SemaRef.getLangOpts());
SemaRef.diagnoseTypo(TC, SemaRef.PDiag(diag::err_no_member_suggest)
<< Typo << DC << DroppedSpecifier
<< SS.getRange());
} else {
SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << DC << BaseRange;
}
},
[=](Sema &SemaRef, TypoExpr *TE, TypoCorrection TC) mutable {
LookupResult R(Q.SemaRef, Q.NameInfo, Q.LookupKind, Q.Redecl);
R.clear(); // Ensure there's no decls lingering in the shared state.
R.suppressDiagnostics();
R.setLookupName(TC.getCorrection());
for (NamedDecl *ND : TC)
R.addDecl(ND);
R.resolveKind();
return SemaRef.BuildMemberReferenceExpr(
BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(),
nullptr, R, nullptr, nullptr);
},
Sema::CTK_ErrorRecovery, DC);
return false;
}
static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
ExprResult &BaseExpr, bool &IsArrow,
SourceLocation OpLoc, CXXScopeSpec &SS,
Decl *ObjCImpDecl, bool HasTemplateArgs,
SourceLocation TemplateKWLoc);
ExprResult
Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
SourceLocation OpLoc, bool IsArrow,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs,
const Scope *S,
ActOnMemberAccessExtraArgs *ExtraArgs) {
if (BaseType->isDependentType() ||
(SS.isSet() && isDependentScopeSpecifier(SS)))
return ActOnDependentMemberExpr(Base, BaseType,
IsArrow, OpLoc,
SS, TemplateKWLoc, FirstQualifierInScope,
NameInfo, TemplateArgs);
LookupResult R(*this, NameInfo, LookupMemberName);
// Implicit member accesses.
if (!Base) {
TypoExpr *TE = nullptr;
QualType RecordTy = BaseType;
if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType();
if (LookupMemberExprInRecord(
*this, R, nullptr, RecordTy->getAs<RecordType>(), OpLoc, IsArrow,
SS, TemplateArgs != nullptr, TemplateKWLoc, TE))
return ExprError();
if (TE)
return TE;
// Explicit member accesses.
} else {
ExprResult BaseResult = Base;
ExprResult Result =
LookupMemberExpr(*this, R, BaseResult, IsArrow, OpLoc, SS,
ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr,
TemplateArgs != nullptr, TemplateKWLoc);
if (BaseResult.isInvalid())
return ExprError();
Base = BaseResult.get();
if (Result.isInvalid())
return ExprError();
if (Result.get())
return Result;
// LookupMemberExpr can modify Base, and thus change BaseType
BaseType = Base->getType();
}
return BuildMemberReferenceExpr(Base, BaseType,
OpLoc, IsArrow, SS, TemplateKWLoc,
FirstQualifierInScope, R, TemplateArgs, S,
false, ExtraArgs);
}
ExprResult
Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS,
SourceLocation loc,
IndirectFieldDecl *indirectField,
DeclAccessPair foundDecl,
Expr *baseObjectExpr,
SourceLocation opLoc) {
// First, build the expression that refers to the base object.
// 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.get();
}
assert((baseVariable || baseObjectExpr) &&
"referencing anonymous struct/union without a base variable or "
"expression");
// 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();
// Case 2: the base of the indirect field is a field and the user
// wrote a member expression.
if (!baseVariable) {
FieldDecl *field = cast<FieldDecl>(*FI);
bool baseObjectIsPointer = baseObjectExpr->getType()->isPointerType();
// Make a nameInfo that properly uses the anonymous name.
DeclarationNameInfo memberNameInfo(field->getDeclName(), loc);
// Build the first member access in the chain with full information.
result =
BuildFieldReferenceExpr(result, baseObjectIsPointer, SourceLocation(),
SS, field, foundDecl, memberNameInfo)
.get();
if (!result)
return ExprError();
}
// 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 fakeFoundDecl =
DeclAccessPair::make(field, field->getAccess());
result =
BuildFieldReferenceExpr(result, /*isarrow*/ false, SourceLocation(),
(FI == FEnd ? SS : EmptySS), field,
fakeFoundDecl, memberNameInfo)
.get();
}
return result;
}
static ExprResult
BuildMSPropertyRefExpr(Sema &S, Expr *BaseExpr, bool IsArrow,
const CXXScopeSpec &SS,
MSPropertyDecl *PD,
const DeclarationNameInfo &NameInfo) {
// Property names are always simple identifiers and therefore never
// require any interesting additional storage.
return new (S.Context) MSPropertyRefExpr(BaseExpr, PD, IsArrow,
S.Context.PseudoObjectTy, VK_LValue,
SS.getWithLocInContext(S.Context),
NameInfo.getLoc());
}
MemberExpr *Sema::BuildMemberExpr(
Expr *Base, bool IsArrow, SourceLocation OpLoc, const CXXScopeSpec *SS,
SourceLocation TemplateKWLoc, ValueDecl *Member, DeclAccessPair FoundDecl,
bool HadMultipleCandidates, const DeclarationNameInfo &MemberNameInfo,
QualType Ty, ExprValueKind VK, ExprObjectKind OK,
const TemplateArgumentListInfo *TemplateArgs) {
NestedNameSpecifierLoc NNS =
SS ? SS->getWithLocInContext(Context) : NestedNameSpecifierLoc();
return BuildMemberExpr(Base, IsArrow, OpLoc, NNS, TemplateKWLoc, Member,
FoundDecl, HadMultipleCandidates, MemberNameInfo, Ty,
VK, OK, TemplateArgs);
}
MemberExpr *Sema::BuildMemberExpr(
Expr *Base, bool IsArrow, SourceLocation OpLoc, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, ValueDecl *Member, DeclAccessPair FoundDecl,
bool HadMultipleCandidates, const DeclarationNameInfo &MemberNameInfo,
QualType Ty, ExprValueKind VK, ExprObjectKind OK,
const TemplateArgumentListInfo *TemplateArgs) {
assert((!IsArrow || Base->isRValue()) && "-> base must be a pointer rvalue");
MemberExpr *E =
MemberExpr::Create(Context, Base, IsArrow, OpLoc, NNS, TemplateKWLoc,
Member, FoundDecl, MemberNameInfo, TemplateArgs, Ty,
VK, OK, getNonOdrUseReasonInCurrentContext(Member));
E->setHadMultipleCandidates(HadMultipleCandidates);
MarkMemberReferenced(E);
// C++ [except.spec]p17:
// An exception-specification is considered to be needed when:
// - in an expression the function is the unique lookup result or the
// selected member of a set of overloaded functions
if (auto *FPT = Ty->getAs<FunctionProtoType>()) {
if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
if (auto *NewFPT = ResolveExceptionSpec(MemberNameInfo.getLoc(), FPT))
E->setType(Context.getQualifiedType(NewFPT, Ty.getQualifiers()));
}
}
return E;
}
/// Determine if the given scope is within a function-try-block handler.
static bool IsInFnTryBlockHandler(const Scope *S) {
// Walk the scope stack until finding a FnTryCatchScope, or leave the
// function scope. If a FnTryCatchScope is found, check whether the TryScope
// flag is set. If it is not, it's a function-try-block handler.
for (; S != S->getFnParent(); S = S->getParent()) {
if (S->getFlags() & Scope::FnTryCatchScope)
return (S->getFlags() & Scope::TryScope) != Scope::TryScope;
}
return false;
}
ExprResult
Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
SourceLocation OpLoc, bool IsArrow,
const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs,
const Scope *S,
bool SuppressQualifierCheck,
ActOnMemberAccessExtraArgs *ExtraArgs) {
QualType BaseType = BaseExprType;
if (IsArrow) {
assert(BaseType->isPointerType());
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
}
R.setBaseObjectType(BaseType);
// C++1z [expr.ref]p2:
// For the first option (dot) the first expression shall be a glvalue [...]
if (!IsArrow && BaseExpr && BaseExpr->isRValue()) {
ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
if (Converted.isInvalid())
return ExprError();
BaseExpr = Converted.get();
}
const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo();
DeclarationName MemberName = MemberNameInfo.getName();
SourceLocation MemberLoc = MemberNameInfo.getLoc();
if (R.isAmbiguous())
return ExprError();
// [except.handle]p10: Referring to any non-static member or base class of an
// object in the handler for a function-try-block of a constructor or
// destructor for that object results in undefined behavior.
const auto *FD = getCurFunctionDecl();
if (S && BaseExpr && FD &&
(isa<CXXDestructorDecl>(FD) || isa<CXXConstructorDecl>(FD)) &&
isa<CXXThisExpr>(BaseExpr->IgnoreImpCasts()) &&
IsInFnTryBlockHandler(S))
Diag(MemberLoc, diag::warn_cdtor_function_try_handler_mem_expr)
<< isa<CXXDestructorDecl>(FD);
if (R.empty()) {
// Rederive where we looked up.
DeclContext *DC = (SS.isSet()
? computeDeclContext(SS, false)
: BaseType->castAs<RecordType>()->getDecl());
if (ExtraArgs) {
ExprResult RetryExpr;
if (!IsArrow && BaseExpr) {
SFINAETrap Trap(*this, true);
ParsedType ObjectType;
bool MayBePseudoDestructor = false;
RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr,
OpLoc, tok::arrow, ObjectType,
MayBePseudoDestructor);
if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) {
CXXScopeSpec TempSS(SS);
RetryExpr = ActOnMemberAccessExpr(
ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS,
TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl);
}
if (Trap.hasErrorOccurred())
RetryExpr = ExprError();
}
if (RetryExpr.isUsable()) {
Diag(OpLoc, diag::err_no_member_overloaded_arrow)
<< MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->");
return RetryExpr;
}
}
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),
TemplateKWLoc, MemberNameInfo,
TemplateArgs, R.begin(), R.end());
return 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()) {
// We might have a variable template specialization (or maybe one day a
// member concept-id).
if (TemplateArgs || TemplateKWLoc.isValid())
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/false, TemplateArgs);
return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl,
FoundDecl, TemplateArgs);
}
SourceLocation Loc = R.getNameLoc();
if (SS.getRange().isValid())
Loc = SS.getRange().getBegin();
BaseExpr = BuildCXXThisExpr(Loc, BaseExprType, /*IsImplicit=*/true);
}
// Check the use of this member.
if (DiagnoseUseOfDecl(MemberDecl, MemberLoc))
return ExprError();
if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl))
return BuildFieldReferenceExpr(BaseExpr, IsArrow, OpLoc, SS, FD, FoundDecl,
MemberNameInfo);
if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl))
return BuildMSPropertyRefExpr(*this, BaseExpr, IsArrow, SS, PD,
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,
FoundDecl, BaseExpr,
OpLoc);
if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) {
return BuildMemberExpr(BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Var,
FoundDecl, /*HadMultipleCandidates=*/false,
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();
}
return BuildMemberExpr(BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc,
MemberFn, FoundDecl, /*HadMultipleCandidates=*/false,
MemberNameInfo, type, valueKind, OK_Ordinary);
}
assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?");
if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) {
return BuildMemberExpr(BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Enum,
FoundDecl, /*HadMultipleCandidates=*/false,
MemberNameInfo, Enum->getType(), VK_RValue,
OK_Ordinary);
}
if (VarTemplateDecl *VarTempl = dyn_cast<VarTemplateDecl>(MemberDecl)) {
if (!TemplateArgs) {
diagnoseMissingTemplateArguments(TemplateName(VarTempl), MemberLoc);
return ExprError();
}
DeclResult VDecl = CheckVarTemplateId(VarTempl, TemplateKWLoc,
MemberNameInfo.getLoc(), *TemplateArgs);
if (VDecl.isInvalid())
return ExprError();
// Non-dependent member, but dependent template arguments.
if (!VDecl.get())
return ActOnDependentMemberExpr(
BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc,
FirstQualifierInScope, MemberNameInfo, TemplateArgs);
VarDecl *Var = cast<VarDecl>(VDecl.get());
if (!Var->getTemplateSpecializationKind())
Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation, MemberLoc);
return BuildMemberExpr(
BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Var, FoundDecl,
/*HadMultipleCandidates=*/false, MemberNameInfo,
Var->getType().getNonReferenceType(), VK_LValue, OK_Ordinary);
}
// 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();
}
/// Given that normal member access failed on the given expression,
/// and given that the expression's type involves builtin-id or
/// builtin-Class, decide whether substituting in the redefinition
/// types would be profitable. The redefinition type is whatever
/// this translation unit tried to typedef to id/Class; we store
/// it to the side and then re-use it in places like this.
static bool ShouldTryAgainWithRedefinitionType(Sema &S, ExprResult &base) {
const ObjCObjectPointerType *opty
= base.get()->getType()->getAs<ObjCObjectPointerType>();
if (!opty) return false;
const ObjCObjectType *ty = opty->getObjectType();
QualType redef;
if (ty->isObjCId()) {
redef = S.Context.getObjCIdRedefinitionType();
} else if (ty->isObjCClass()) {
redef = S.Context.getObjCClassRedefinitionType();
} else {
return false;
}
// Do the substitution as long as the redefinition type isn't just a
// possibly-qualified pointer to builtin-id or builtin-Class again.
opty = redef->getAs<ObjCObjectPointerType>();
if (opty && !opty->getObjectType()->getInterface())
return false;
base = S.ImpCastExprToType(base.get(), redef, CK_BitCast);
return true;
}
static bool isRecordType(QualType T) {
return T->isRecordType();
}
static bool isPointerToRecordType(QualType T) {
if (const PointerType *PT = T->getAs<PointerType>())
return PT->getPointeeType()->isRecordType();
return false;
}
/// Perform conversions on the LHS of a member access expression.
ExprResult
Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) {
if (IsArrow && !Base->getType()->isFunctionType())
return DefaultFunctionArrayLvalueConversion(Base);
return CheckPlaceholderExpr(Base);
}
/// Look up the given member of the given non-type-dependent
/// expression. This can return in one of two ways:
/// * If it returns a sentinel null-but-valid result, the caller will
/// assume that lookup was performed and the results written into
/// the provided structure. It will take over from there.
/// * Otherwise, the returned expression will be produced in place of
/// an ordinary member expression.
///
/// The ObjCImpDecl bit is a gross hack that will need to be properly
/// fixed for ObjC++.
static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
ExprResult &BaseExpr, bool &IsArrow,
SourceLocation OpLoc, CXXScopeSpec &SS,
Decl *ObjCImpDecl, bool HasTemplateArgs,
SourceLocation TemplateKWLoc) {
assert(BaseExpr.get() && "no base expression");
// Perform default conversions.
BaseExpr = S.PerformMemberExprBaseConversion(BaseExpr.get(), IsArrow);
if (BaseExpr.isInvalid())
return ExprError();
QualType BaseType = BaseExpr.get()->getType();
assert(!BaseType->isDependentType());
DeclarationName MemberName = R.getLookupName();
SourceLocation MemberLoc = R.getNameLoc();
// For later type-checking purposes, turn arrow accesses into dot
// accesses. The only access type we support that doesn't follow
// the C equivalence "a->b === (*a).b" is ObjC property accesses,
// and those never use arrows, so this is unaffected.
if (IsArrow) {
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
BaseType = Ptr->getPointeeType();
else if (const ObjCObjectPointerType *Ptr
= BaseType->getAs<ObjCObjectPointerType>())
BaseType = Ptr->getPointeeType();
else if (BaseType->isRecordType()) {
// Recover from arrow accesses to records, e.g.:
// struct MyRecord foo;
// foo->bar
// This is actually well-formed in C++ if MyRecord has an
// overloaded operator->, but that should have been dealt with
// by now--or a diagnostic message already issued if a problem
// was encountered while looking for the overloaded operator->.
if (!S.getLangOpts().CPlusPlus) {
S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
<< BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange()
<< FixItHint::CreateReplacement(OpLoc, ".");
}
IsArrow = false;
} else if (BaseType->isFunctionType()) {
goto fail;
} else {
S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow)
<< BaseType << BaseExpr.get()->getSourceRange();
return ExprError();
}
}
// Handle field access to simple records.
if (const RecordType *RTy = BaseType->getAs<RecordType>()) {
TypoExpr *TE = nullptr;
if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS,
HasTemplateArgs, TemplateKWLoc, TE))
return ExprError();
// Returning valid-but-null is how we indicate to the caller that
// the lookup result was filled in. If typo correction was attempted and
// failed, the lookup result will have been cleared--that combined with the
// valid-but-null ExprResult will trigger the appropriate diagnostics.
return ExprResult(TE);
}
// Handle ivar access to Objective-C objects.
if (const ObjCObjectType *OTy = BaseType->getAs<ObjCObjectType>()) {
if (!SS.isEmpty() && !SS.isInvalid()) {
S.Diag(SS.getRange().getBegin(), diag::err_qualified_objc_access)
<< 1 << SS.getScopeRep()
<< FixItHint::CreateRemoval(SS.getRange());
SS.clear();
}
IdentifierInfo *Member = MemberName.getAsIdentifierInfo();
// There are three cases for the base type:
// - builtin id (qualified or unqualified)
// - builtin Class (qualified or unqualified)
// - an interface
ObjCInterfaceDecl *IDecl = OTy->getInterface();
if (!IDecl) {
if (S.getLangOpts().ObjCAutoRefCount &&
(OTy->isObjCId() || OTy->isObjCClass()))
goto fail;
// There's an implicit 'isa' ivar on all objects.
// But we only actually find it this way on objects of type 'id',
// apparently.
if (OTy->isObjCId() && Member->isStr("isa"))
return new (S.Context) ObjCIsaExpr(BaseExpr.get(), IsArrow, MemberLoc,
OpLoc, S.Context.getObjCClassType());
if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
goto fail;
}
if (S.RequireCompleteType(OpLoc, BaseType,
diag::err_typecheck_incomplete_tag,
BaseExpr.get()))
return ExprError();
ObjCInterfaceDecl *ClassDeclared = nullptr;
ObjCIvarDecl *IV = IDecl->lookupInstanceVariable(Member, ClassDeclared);
if (!IV) {
// Attempt to correct for typos in ivar names.
DeclFilterCCC<ObjCIvarDecl> Validator{};
Validator.IsObjCIvarLookup = IsArrow;
if (TypoCorrection Corrected = S.CorrectTypo(
R.getLookupNameInfo(), Sema::LookupMemberName, nullptr, nullptr,
Validator, Sema::CTK_ErrorRecovery, IDecl)) {
IV = Corrected.getCorrectionDeclAs<ObjCIvarDecl>();
S.diagnoseTypo(
Corrected,
S.PDiag(diag::err_typecheck_member_reference_ivar_suggest)
<< IDecl->getDeclName() << MemberName);
// Figure out the class that declares the ivar.
assert(!ClassDeclared);
Decl *D = cast<Decl>(IV->getDeclContext());
if (auto *Category = dyn_cast<ObjCCategoryDecl>(D))
D = Category->getClassInterface();
if (auto *Implementation = dyn_cast<ObjCImplementationDecl>(D))
ClassDeclared = Implementation->getClassInterface();
else if (auto *Interface = dyn_cast<ObjCInterfaceDecl>(D))
ClassDeclared = Interface;
assert(ClassDeclared && "cannot query interface");
} else {
if (IsArrow &&
IDecl->FindPropertyDeclaration(
Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) {
S.Diag(MemberLoc, diag::err_property_found_suggest)
<< Member << BaseExpr.get()->getType()
<< FixItHint::CreateReplacement(OpLoc, ".");
return ExprError();
}
S.Diag(MemberLoc, diag::err_typecheck_member_reference_ivar)
<< IDecl->getDeclName() << MemberName
<< BaseExpr.get()->getSourceRange();
return ExprError();
}
}
assert(ClassDeclared);
// 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 (IV->isInvalidDecl())
return ExprError();
// Check whether we can reference this field.
if (S.DiagnoseUseOfDecl(IV, MemberLoc))
return ExprError();
if (IV->getAccessControl() != ObjCIvarDecl::Public &&
IV->getAccessControl() != ObjCIvarDecl::Package) {
ObjCInterfaceDecl *ClassOfMethodDecl = nullptr;
if (ObjCMethodDecl *MD = S.getCurMethodDecl())
ClassOfMethodDecl = MD->getClassInterface();
else if (ObjCImpDecl && S.getCurFunctionDecl()) {
// Case of a c-function declared inside an objc implementation.
// FIXME: For a c-style function nested inside an objc implementation
// class, there is no implementation context available, so we pass
// down the context as argument to this routine. Ideally, this context
// need be passed down in the AST node and somehow calculated from the
// AST for a function decl.
if (ObjCImplementationDecl *IMPD =
dyn_cast<ObjCImplementationDecl>(ObjCImpDecl))
ClassOfMethodDecl = IMPD->getClassInterface();
else if (ObjCCategoryImplDecl* CatImplClass =
dyn_cast<ObjCCategoryImplDecl>(ObjCImpDecl))
ClassOfMethodDecl = CatImplClass->getClassInterface();
}
if (!S.getLangOpts().DebuggerSupport) {
if (IV->getAccessControl() == ObjCIvarDecl::Private) {
if (!declaresSameEntity(ClassDeclared, IDecl) ||
!declaresSameEntity(ClassOfMethodDecl, ClassDeclared))
S.Diag(MemberLoc, diag::err_private_ivar_access)
<< IV->getDeclName();
} else if (!IDecl->isSuperClassOf(ClassOfMethodDecl))
// @protected
S.Diag(MemberLoc, diag::err_protected_ivar_access)
<< IV->getDeclName();
}
}
bool warn = true;
if (S.getLangOpts().ObjCWeak) {
Expr *BaseExp = BaseExpr.get()->IgnoreParenImpCasts();
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BaseExp))
if (UO->getOpcode() == UO_Deref)
BaseExp = UO->getSubExpr()->IgnoreParenCasts();
if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(BaseExp))
if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
S.Diag(DE->getLocation(), diag::err_arc_weak_ivar_access);
warn = false;
}
}
if (warn) {
if (ObjCMethodDecl *MD = S.getCurMethodDecl()) {
ObjCMethodFamily MF = MD->getMethodFamily();
warn = (MF != OMF_init && MF != OMF_dealloc &&
MF != OMF_finalize &&
!S.IvarBacksCurrentMethodAccessor(IDecl, MD, IV));
}
if (warn)
S.Diag(MemberLoc, diag::warn_direct_ivar_access) << IV->getDeclName();
}
ObjCIvarRefExpr *Result = new (S.Context) ObjCIvarRefExpr(
Substitute type arguments into uses of Objective-C interface members. When messaging a method that was defined in an Objective-C class (or category or extension thereof) that has type parameters, substitute the type arguments for those type parameters. Similarly, substitute into property accesses, instance variables, and other references. This includes general infrastructure for substituting the type arguments associated with an ObjCObject(Pointer)Type into a type referenced within a particular context, handling all of the substitutions required to deal with (e.g.) inheritance involving parameterized classes. In cases where no type arguments are available (e.g., because we're messaging via some unspecialized type, id, etc.), we substitute in the type bounds for the type parameters instead. Example: @interface NSSet<T : id<NSCopying>> : NSObject <NSCopying> - (T)firstObject; @end void f(NSSet<NSString *> *stringSet, NSSet *anySet) { [stringSet firstObject]; // produces NSString* [anySet firstObject]; // produces id<NSCopying> (the bound) } When substituting for the type parameters given an unspecialized context (i.e., no specific type arguments were given), substituting the type bounds unconditionally produces type signatures that are too strong compared to the pre-generics signatures. Instead, use the following rule: - In covariant positions, such as method return types, replace type parameters with “id” or “Class” (the latter only when the type parameter bound is “Class” or qualified class, e.g, “Class<NSCopying>”) - In other positions (e.g., parameter types), replace type parameters with their type bounds. - When a specialized Objective-C object or object pointer type contains a type parameter in its type arguments (e.g., NSArray<T>*, but not NSArray<NSString *> *), replace the entire object/object pointer type with its unspecialized version (e.g., NSArray *). llvm-svn: 241543
2015-07-07 11:57:53 +08:00
IV, IV->getUsageType(BaseType), MemberLoc, OpLoc, BaseExpr.get(),
IsArrow);
if (IV->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
if (!S.isUnevaluatedContext() &&
!S.Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, MemberLoc))
S.getCurFunction()->recordUseOfWeak(Result);
}
return Result;
}
// Objective-C property access.
const ObjCObjectPointerType *OPT;
if (!IsArrow && (OPT = BaseType->getAs<ObjCObjectPointerType>())) {
if (!SS.isEmpty() && !SS.isInvalid()) {
S.Diag(SS.getRange().getBegin(), diag::err_qualified_objc_access)
<< 0 << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange());
SS.clear();
}
// This actually uses the base as an r-value.
BaseExpr = S.DefaultLvalueConversion(BaseExpr.get());
if (BaseExpr.isInvalid())
return ExprError();
assert(S.Context.hasSameUnqualifiedType(BaseType,
BaseExpr.get()->getType()));
IdentifierInfo *Member = MemberName.getAsIdentifierInfo();
const ObjCObjectType *OT = OPT->getObjectType();
// id, with and without qualifiers.
if (OT->isObjCId()) {
// Check protocols on qualified interfaces.
Selector Sel = S.PP.getSelectorTable().getNullarySelector(Member);
if (Decl *PMDecl =
FindGetterSetterNameDecl(OPT, Member, Sel, S.Context)) {
if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(PMDecl)) {
// Check the use of this declaration
if (S.DiagnoseUseOfDecl(PD, MemberLoc))
return ExprError();
return new (S.Context)
ObjCPropertyRefExpr(PD, S.Context.PseudoObjectTy, VK_LValue,
OK_ObjCProperty, MemberLoc, BaseExpr.get());
}
if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(PMDecl)) {
Selector SetterSel =
SelectorTable::constructSetterSelector(S.PP.getIdentifierTable(),
S.PP.getSelectorTable(),
Member);
ObjCMethodDecl *SMD = nullptr;
if (Decl *SDecl = FindGetterSetterNameDecl(OPT,
/*Property id*/ nullptr,
SetterSel, S.Context))
SMD = dyn_cast<ObjCMethodDecl>(SDecl);
return new (S.Context)
ObjCPropertyRefExpr(OMD, SMD, S.Context.PseudoObjectTy, VK_LValue,
OK_ObjCProperty, MemberLoc, BaseExpr.get());
}
}
// Use of id.member can only be for a property reference. Do not
// use the 'id' redefinition in this case.
if (IsArrow && ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
return ExprError(S.Diag(MemberLoc, diag::err_property_not_found)
<< MemberName << BaseType);
}
// 'Class', unqualified only.
if (OT->isObjCClass()) {
// Only works in a method declaration (??!).
ObjCMethodDecl *MD = S.getCurMethodDecl();
if (!MD) {
if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
goto fail;
}
// Also must look for a getter name which uses property syntax.
Selector Sel = S.PP.getSelectorTable().getNullarySelector(Member);
ObjCInterfaceDecl *IFace = MD->getClassInterface();
if (!IFace)
goto fail;
ObjCMethodDecl *Getter;
if ((Getter = IFace->lookupClassMethod(Sel))) {
// Check the use of this method.
if (S.DiagnoseUseOfDecl(Getter, MemberLoc))
return ExprError();
} else
Getter = IFace->lookupPrivateMethod(Sel, false);
// If we found a getter then this may be a valid dot-reference, we
// will look for the matching setter, in case it is needed.
Selector SetterSel =
SelectorTable::constructSetterSelector(S.PP.getIdentifierTable(),
S.PP.getSelectorTable(),
Member);
ObjCMethodDecl *Setter = IFace->lookupClassMethod(SetterSel);
if (!Setter) {
// If this reference is in an @implementation, also check for 'private'
// methods.
Setter = IFace->lookupPrivateMethod(SetterSel, false);
}
if (Setter && S.DiagnoseUseOfDecl(Setter, MemberLoc))
return ExprError();
if (Getter || Setter) {
return new (S.Context) ObjCPropertyRefExpr(
Getter, Setter, S.Context.PseudoObjectTy, VK_LValue,
OK_ObjCProperty, MemberLoc, BaseExpr.get());
}
if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
return ExprError(S.Diag(MemberLoc, diag::err_property_not_found)
<< MemberName << BaseType);
}
// Normal property access.
return S.HandleExprPropertyRefExpr(OPT, BaseExpr.get(), OpLoc, MemberName,
MemberLoc, SourceLocation(), QualType(),
false);
}
// Handle 'field access' to vectors, such as 'V.xx'.
if (BaseType->isExtVectorType()) {
// FIXME: this expr should store IsArrow.
IdentifierInfo *Member = MemberName.getAsIdentifierInfo();
ExprValueKind VK;
if (IsArrow)
VK = VK_LValue;
else {
if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(BaseExpr.get()))
VK = POE->getSyntacticForm()->getValueKind();
else
VK = BaseExpr.get()->getValueKind();
}
QualType ret = CheckExtVectorComponent(S, BaseType, VK, OpLoc,
Member, MemberLoc);
if (ret.isNull())
return ExprError();
Qualifiers BaseQ =
S.Context.getCanonicalType(BaseExpr.get()->getType()).getQualifiers();
ret = S.Context.getQualifiedType(ret, BaseQ);
return new (S.Context)
ExtVectorElementExpr(ret, VK, BaseExpr.get(), *Member, MemberLoc);
}
// Adjust builtin-sel to the appropriate redefinition type if that's
// not just a pointer to builtin-sel again.
if (IsArrow && BaseType->isSpecificBuiltinType(BuiltinType::ObjCSel) &&
!S.Context.getObjCSelRedefinitionType()->isObjCSelType()) {
BaseExpr = S.ImpCastExprToType(
BaseExpr.get(), S.Context.getObjCSelRedefinitionType(), CK_BitCast);
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
}
// Failure cases.
fail:
// Recover from dot accesses to pointers, e.g.:
// type *foo;
// foo.bar
// This is actually well-formed in two cases:
// - 'type' is an Objective C type
// - 'bar' is a pseudo-destructor name which happens to refer to
// the appropriate pointer type
if (const PointerType *Ptr = BaseType->getAs<PointerType>()) {
if (!IsArrow && Ptr->getPointeeType()->isRecordType() &&
MemberName.getNameKind() != DeclarationName::CXXDestructorName) {
S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
<< BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange()
<< FixItHint::CreateReplacement(OpLoc, "->");
// Recurse as an -> access.
IsArrow = true;
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
}
}
// If the user is trying to apply -> or . to a function name, it's probably
// because they forgot parentheses to call that function.
if (S.tryToRecoverWithCall(
BaseExpr, S.PDiag(diag::err_member_reference_needs_call),
/*complain*/ false,
IsArrow ? &isPointerToRecordType : &isRecordType)) {
if (BaseExpr.isInvalid())
return ExprError();
BaseExpr = S.DefaultFunctionArrayConversion(BaseExpr.get());
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
}
S.Diag(OpLoc, diag::err_typecheck_member_reference_struct_union)
<< BaseType << BaseExpr.get()->getSourceRange() << MemberLoc;
return ExprError();
}
/// The main callback when the parser finds something like
/// expression . [nested-name-specifier] identifier
/// expression -> [nested-name-specifier] identifier
/// where 'identifier' encompasses a fairly broad spectrum of
/// possibilities, including destructor and operator references.
///
/// \param OpKind either tok::arrow or tok::period
/// \param ObjCImpDecl the current Objective-C \@implementation
/// decl; this is an ugly hack around the fact that Objective-C
/// \@implementations aren't properly put in the context chain
ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &Id,
Decl *ObjCImpDecl) {
if (SS.isSet() && SS.isInvalid())
return ExprError();
// Warn about the explicit constructor calls Microsoft extension.
if (getLangOpts().MicrosoftExt &&
Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)
Diag(Id.getSourceRange().getBegin(),
diag::ext_ms_explicit_constructor_call);
TemplateArgumentListInfo TemplateArgsBuffer;
// Decompose the name into its component parts.
DeclarationNameInfo NameInfo;
const TemplateArgumentListInfo *TemplateArgs;
DecomposeUnqualifiedId(Id, TemplateArgsBuffer,
NameInfo, TemplateArgs);
DeclarationName Name = NameInfo.getName();
bool IsArrow = (OpKind == tok::arrow);
NamedDecl *FirstQualifierInScope
= (!SS.isSet() ? nullptr : FindFirstQualifierInScope(S, SS.getScopeRep()));
// This is a postfix expression, so get rid of ParenListExprs.
ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base);
if (Result.isInvalid()) return ExprError();
Base = Result.get();
if (Base->getType()->isDependentType() || Name.isDependentName() ||
isDependentScopeSpecifier(SS)) {
return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS,
TemplateKWLoc, FirstQualifierInScope,
NameInfo, TemplateArgs);
}
ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
ExprResult Res = BuildMemberReferenceExpr(
Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
return Res;
}
void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) {
if (isUnevaluatedContext())
return;
QualType ResultTy = E->getType();
// Member accesses have four cases:
// 1: non-array member via "->": dereferences
// 2: non-array member via ".": nothing interesting happens
// 3: array member access via "->": nothing interesting happens
// (this returns an array lvalue and does not actually dereference memory)
// 4: array member access via ".": *adds* a layer of indirection
if (ResultTy->isArrayType()) {
if (!E->isArrow()) {
// This might be something like:
// (*structPtr).arrayMember
// which behaves roughly like:
// &(*structPtr).pointerMember
// in that the apparent dereference in the base expression does not
// actually happen.
CheckAddressOfNoDeref(E->getBase());
}
} else if (E->isArrow()) {
if (const auto *Ptr = dyn_cast<PointerType>(
E->getBase()->getType().getDesugaredType(Context))) {
if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
ExprEvalContexts.back().PossibleDerefs.insert(E);
}
}
}
ExprResult
Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow,
SourceLocation OpLoc, const CXXScopeSpec &SS,
FieldDecl *Field, DeclAccessPair FoundDecl,
const DeclarationNameInfo &MemberNameInfo) {
// x.a is an l-value if 'a' has a reference type. Otherwise:
// x.a is an l-value/x-value/pr-value if the base is (and note
// that *x is always an l-value), except that if the base isn't
// an ordinary object then we must have an rvalue.
ExprValueKind VK = VK_LValue;
ExprObjectKind OK = OK_Ordinary;
if (!IsArrow) {
if (BaseExpr->getObjectKind() == OK_Ordinary)
VK = BaseExpr->getValueKind();
else
VK = VK_RValue;
}
if (VK != VK_RValue && Field->isBitField())
OK = OK_BitField;
// Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref]
QualType MemberType = Field->getType();
if (const ReferenceType *Ref = MemberType->getAs<ReferenceType>()) {
MemberType = Ref->getPointeeType();
VK = VK_LValue;
} else {
QualType BaseType = BaseExpr->getType();
if (IsArrow) BaseType = BaseType->getAs<PointerType>()->getPointeeType();
Qualifiers BaseQuals = BaseType.getQualifiers();
// GC attributes are never picked up by members.
BaseQuals.removeObjCGCAttr();
// CVR attributes from the base are picked up by members,
// except that 'mutable' members don't pick up 'const'.
if (Field->isMutable()) BaseQuals.removeConst();
Qualifiers MemberQuals =
Context.getCanonicalType(MemberType).getQualifiers();
assert(!MemberQuals.hasAddressSpace());
Qualifiers Combined = BaseQuals + MemberQuals;
if (Combined != MemberQuals)
MemberType = Context.getQualifiedType(MemberType, Combined);
// Pick up NoDeref from the base in case we end up using AddrOf on the
// result. E.g. the expression
// &someNoDerefPtr->pointerMember
// should be a noderef pointer again.
if (BaseType->hasAttr(attr::NoDeref))
MemberType =
Context.getAttributedType(attr::NoDeref, MemberType, MemberType);
}
auto *CurMethod = dyn_cast<CXXMethodDecl>(CurContext);
if (!(CurMethod && CurMethod->isDefaulted()))
UnusedPrivateFields.remove(Field);
ExprResult Base = PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(),
FoundDecl, Field);
if (Base.isInvalid())
return ExprError();
// Build a reference to a private copy for non-static data members in
// non-static member functions, privatized by OpenMP constructs.
if (getLangOpts().OpenMP && IsArrow &&
!CurContext->isDependentContext() &&
isa<CXXThisExpr>(Base.get()->IgnoreParenImpCasts())) {
if (auto *PrivateCopy = isOpenMPCapturedDecl(Field)) {
return getOpenMPCapturedExpr(PrivateCopy, VK, OK,
MemberNameInfo.getLoc());
}
}
return BuildMemberExpr(Base.get(), IsArrow, OpLoc, &SS,
/*TemplateKWLoc=*/SourceLocation(), Field, FoundDecl,
/*HadMultipleCandidates=*/false, MemberNameInfo,
MemberType, VK, OK);
}
/// Builds an implicit member access expression. The current context
/// is known to be an instance method, and the given unqualified lookup
/// set is known to contain only instance members, at least one of which
/// is from an appropriate type.
ExprResult
Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs,
bool IsKnownInstance, const Scope *S) {
assert(!R.empty() && !R.isAmbiguous());
SourceLocation loc = R.getNameLoc();
// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
QualType ThisTy = getCurrentThisType();
assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'");
Expr *baseExpr = nullptr; // null signifies implicit access
if (IsKnownInstance) {
SourceLocation Loc = R.getNameLoc();
if (SS.getRange().isValid())
Loc = SS.getRange().getBegin();
baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
}
return BuildMemberReferenceExpr(baseExpr, ThisTy,
/*OpLoc*/ SourceLocation(),
/*IsArrow*/ true,
SS, TemplateKWLoc,
/*FirstQualifierInScope*/ nullptr,
R, TemplateArgs, S);
}