text:
//===--- 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"
#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) {
  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 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);
  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;
}

VarDecl *
Sema::getVarTemplateSpecialization(VarTemplateDecl *VarTempl,
                      const TemplateArgumentListInfo *TemplateArgs,
                      const DeclarationNameInfo &MemberNameInfo,
                      SourceLocation TemplateKWLoc) {
  if (!TemplateArgs) {
    diagnoseMissingTemplateArguments(TemplateName(VarTempl),
                                     MemberNameInfo.getBeginLoc());
    return nullptr;
  }

  DeclResult VDecl = CheckVarTemplateId(VarTempl, TemplateKWLoc,
                                        MemberNameInfo.getLoc(), *TemplateArgs);
  if (VDecl.isInvalid())
    return nullptr;
  VarDecl *Var = cast<VarDecl>(VDecl.get());
  if (!Var->getTemplateSpecializationKind())
    Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation,
                                       MemberNameInfo.getLoc());
  return Var;
}

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()) {
      // If this is a variable template, get the instantiated variable
      // declaration corresponding to the supplied template arguments
      // (while emitting diagnostics as necessary) that will be referenced
      // by this expression.
      assert((!TemplateArgs || isa<VarTemplateDecl>(MemberDecl)) &&
             "How did we get template arguments here sans a variable template");
      if (isa<VarTemplateDecl>(MemberDecl)) {
        MemberDecl = getVarTemplateSpecialization(
            cast<VarTemplateDecl>(MemberDecl), TemplateArgs,
            R.getLookupNameInfo(), TemplateKWLoc);
        if (!MemberDecl)
          return ExprError();
      }
      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 (VarDecl *Var = getVarTemplateSpecialization(
            VarTempl, TemplateArgs, MemberNameInfo, TemplateKWLoc))
      return BuildMemberExpr(
          BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Var, FoundDecl,
          /*HadMultipleCandidates=*/false, MemberNameInfo,
          Var->getType().getNonReferenceType(), VK_LValue, OK_Ordinary);
    return ExprError();
  }

  // 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(
        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) {
  QualType ResultTy = E->getType();

  // Do not warn on member accesses to arrays since this returns an array
  // lvalue and does not actually dereference memory.
  if (isa<ArrayType>(ResultTy))
    return;

  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);
  }

  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.
  // '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);
}


text:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title> - Articles</title>
    <description>Quick drops of Devops knowledge for everyday use.</description>
    <link>
    </link>

      <item>
        <title>Welcome to Ghost</title>

          <description>
Hey! Welcome to Ghost, it's great to have you :)

</description>

        <pubDate>Thu, 27 Jul 2017 12:00:00 +0200</pubDate>
        <link>
        /welcome</link>
        <guid isPermaLink="true">/welcome</guid>
      </item>

      <item>
        <title>Using the Ghost editor</title>

          <description>
Ghost uses a language called Markdown to format text.

When you go to edit a post and see special characters and colours intertwined between the words, those are Markdown shortcuts which tell Ghost what to do with the words in your document. The biggest benefit of Markdown is that you can quickly apply formatting as you type, without needing to pause.

At the bottom of the editor, you'll find a toolbar with basic formatting options to help you get started as easily as possible. You'll also notice that there's a ? icon, which contains more advanced shortcuts.

For now, though, let's run you through some of the basics. You'll want to make sure you're editing this post in order to see all the Markdown we've used.

Formatting text

The most common shortcuts are of course, bold text, italic text, and hyperlinks. These generally make up the bulk of any document. You can type the characters out, but you can also use keyboard shortcuts.

CMD/Ctrl + B for Bold

CMD/Ctrl + I for Italic

CMD/Ctrl + K for a Link

CMD/Ctrl + H for a Heading (Press multiple times for h2/h3/h4/etc)

With just a couple of extra characters here and there, you're well on your way to creating a beautifully formatted story.

Inserting images

Images in Markdown look just the same as links, except they're prefixed with an exclamation mark, like this:

![Image description](/path/to/image.jpg)

Most Markdown editors don't make you type this out, though. In Ghost you can click on the image icon in the toolbar at the bottom of the editor, or you can just click and drag an image from your desktop directly into the editor. Both will upload the image for you and generate the appropriate Markdown.

Important Note: Ghost does not currently have automatic image resizing, so it's always a good idea to make sure your images aren't gigantic files before uploading them to Ghost.

Making lists

Lists in HTML are a formatting nightmare, but in Markdown they become an absolute breeze with just a couple of characters and a bit of smart automation. For numbered lists, just write out the numbers. For bullet lists, just use * or - or +. Like this:

Crack the eggs over a bowl

Whisk them together

Make an omellete

or

Remember to buy milk

Feed the cat

Come up with idea for next story

Adding quotes

When you want to pull out a particularly good except in the middle of a piece, you can use &gt; at the beginning of a paragraph to turn it into a Blockquote. You might've seen this formatting before in email clients.

A well placed quote guides a reader through a story, helping them to understand the most important points being made

All themes handles blockquotes slightly differently. Sometimes they'll look better kept shorter, while other times you can quote fairly hefty amounts of text and get away with it. Generally, the safest option is to use blockquotes sparingly.

Dividing things up

If you're writing a piece in parts and you just feel like you need to divide a couple of sections distinctly from each other, a horizontal rule might be just what you need. Dropping --- on a new line will create a sleak divider, anywhere you want it.

This should get you going with the vast majority of what you need to do in the editor, but if you're still curious about more advanced tips then check out the Advanced Markdown Guide - or if you'd rather learn about how Ghost taxononomies work, we've got a overview of how to use Ghost tags.

</description>

        <pubDate>Thu, 27 Jul 2017 11:00:00 +0200</pubDate>
        <link>
        /the-editor</link>
        <guid isPermaLink="true">/the-editor</guid>
      </item>

      <item>
        <title>Organising your content with tags</title>

          <description>
Ghost has a single, powerful organisational taxonomy, called tags.

It doesn't matter whether you want to call them categories, tags, boxes, or anything else. You can think of Ghost tags a lot like Gmail labels. By tagging posts with one or more keyword, you can organise articles into buckets of related content.

Basic tagging

When you write a post, you can assign tags to help differentiate between categories of content. For example, you might tag some posts with News and other posts with Cycling, which would create two distinct categories of content listed on /tag/news/ and /tag/cycling/, respectively.

If you tag a post with both News and Cycling - then it appears in both sections.

Tag archives are like dedicated home-pages for each category of content that you have. They have their own pages, their own RSS feeds, and can support their own cover images and meta data.

The primary tag

Inside the Ghost editor, you can drag and drop tags into a specific order. The first tag in the list is always given the most importance, and some themes will only display the primary tag (the first tag in the list) by default. So you can add the most important tag which you want to show up in your theme, but also add a bunch of related tags which are less important.

News, Cycling, Bart Stevens, Extreme Sports

In this example, News is the primary tag which will be displayed by the theme, but the post will also still receive all the other tags, and show up in their respective archives.

Private tags

Sometimes you may want to assign a post a specific tag, but you don't necessarily want that tag appearing in the theme or creating an archive page. In Ghost, hashtags are private and can be used for special styling.

For example, if you sometimes publish posts with video content - you might want your theme to adapt and get rid of the sidebar for these posts, to give more space for an embedded video to fill the screen. In this case, you could use private tags to tell your theme what to do.

News, Cycling, #video

Here, the theme would assign the post publicly displayed tags of News, and Cycling - but it would also keep a private record of the post being tagged with #video.

In your theme, you could then look for private tags conditionally and give them special formatting:

{{#post}}
    {{#has tag=&quot;#video&quot;}}
        ...markup for a nice big video post layout...
    {{else}}
        ...regular markup for a post...
    {{/has}}
{{/post}}

You can find documentation for theme development techniques like this and many more over on Ghost's extensive theme documentation.

</description>

        <pubDate>Thu, 27 Jul 2017 10:00:00 +0200</pubDate>
        <link>
        /using-tags</link>
        <guid isPermaLink="true">/using-tags</guid>
      </item>

      <item>
        <title>Managing Ghost users</title>

          <description>
Ghost has a number of different user roles for your team

Authors

The base user level in Ghost is an author. Authors can write posts, edit their own posts, and publish their own posts. Authors are trusted users. If you don't trust users to be allowed to publish their own posts, you shouldn't invite them to Ghost admin.

Editors

Editors are the 2nd user level in Ghost. Editors can do everything that an Author can do, but they can also edit and publish the posts of others - as well as their own. Editors can also invite new authors to the site.

Administrators

The top user level in Ghost is Administrator. Again, administrators can do everything that Authors and Editors can do, but they can also edit all site settings and data, not just content. Additionally, administrators have full access to invite, manage or remove any other user of the site.

The Owner

There is only ever one owner of a Ghost site. The owner is a special user which has all the same permissions as an Administrator, but with two exceptions: The Owner can never be deleted. And in some circumstances the owner will have access to additional special settings if applicable — for example, billing details, if using Ghost(Pro).

It's a good idea to ask all of your users to fill out their user profiles, including bio and social links. These will populate rich structured data for posts and generally create more opportunities for themes to fully populate their design.

</description>

        <pubDate>Thu, 27 Jul 2017 09:00:00 +0200</pubDate>
        <link>
        /managing-users</link>
        <guid isPermaLink="true">/managing-users</guid>
      </item>

      <item>
        <title>Making your site private</title>

          <description>
Sometimes you might want to put your site behind closed doors

If you've got a publication that you don't want the world to see yet because it's not ready to launch, you can hide your Ghost site behind a simple shared pass-phrase.

You can toggle this preference on at the bottom of Ghost's General Settings

Ghost will give you a short, randomly generated pass-phrase which you can share with anyone who needs access to the site while you're working on it. While this setting is enabled, all search engine optimisation features will be switched off to help keep the site off the radar.

Do remember though, this is not secure authentication. You shouldn't rely on this feature for protecting important private data. It's just a simple, shared pass-phrase for very basic privacy.

</description>

        <pubDate>Thu, 27 Jul 2017 08:00:00 +0200</pubDate>
        <link>
        /private-sites</link>
        <guid isPermaLink="true">/private-sites</guid>
      </item>

      <item>
        <title>Advanced Markdown tips</title>

          <description>
There are lots of powerful things you can do with the Ghost editor

If you've gotten pretty comfortable with all the basics of writing in Ghost, then you may enjoy some more advanced tips about the types of things you can do with Markdown!

As with the last post about the editor, you'll want to be actually editing this post as you read it so that you can see all the Markdown code we're using.

Special formatting

As well as bold and italics, you can also use some other special formatting in Markdown when the need arises, for example:

<s>strike through</s>

highlight

*escaped characters*

Writing code blocks

There are two types of code elements which can be inserted in Markdown, the first is inline, and the other is block. Inline code is formatted by wrapping any word or words in back-ticks, like this. Larger snippets of code can be displayed across multiple lines using triple back ticks:

.my-link {
    text-decoration: underline;
}

If you want to get really fancy, you can even add syntax highlighting using Prism.js.

Full bleed images

One neat trick which you can use in Markdown to distinguish between different types of images is to add a #hash value to the end of the source URL, and then target images containing the hash with special styling. For example:

which is styled with...

img[src$=&quot;#full&quot;] {
    max-width: 100vw;
}

This creates full-bleed images in the Casper theme, which stretch beyond their usual boundaries right up to the edge of the window. Every theme handles these types of things slightly differently, but it's a great trick to play with if you want to have a variety of image sizes and styles.

Reference lists

The quick brown fox, jumped over the lazy dog.

Another way to insert links in markdown is using reference lists. You might want to use this style of linking to cite reference material in a Wikipedia-style. All of the links are listed at the end of the document, so you can maintain full separation between content and its source or reference.

Creating footnotes

The quick brown fox[1] jumped over the lazy dog[2].

Footnotes are a great way to add additional contextual details when appropriate. Ghost will automatically add footnote content to the very end of your post.

Full HTML

Perhaps the best part of Markdown is that you're never limited to just Markdown. You can write HTML directly in the Ghost editor and it will just work as HTML usually does. No limits! Here's a standard YouTube embed code as an example:

Foxes are red ↩

Dogs are usually not red ↩

</description>

        <pubDate>Thu, 27 Jul 2017 07:00:00 +0200</pubDate>
        <link>
        /advanced-markdown</link>
        <guid isPermaLink="true">/advanced-markdown</guid>
      </item>

      <item>
        <title>Setting up your own Ghost theme</title>

          <description>
Creating a totally custom design for your publication

Ghost comes with a beautiful default theme called Casper, which is designed to be a clean, readable publication layout and can be easily adapted for most purposes. However, Ghost can also be completely themed to suit your needs. Rather than just giving you a few basic settings which act as a poor proxy for code, we just let you write code.

There are a huge range of both free and premium pre-built themes which you can get from the Ghost Theme Marketplace, or you can simply create your own from scratch.

Anyone can write a completely custom Ghost theme, with just some solid knowledge of HTML and CSS

Ghost themes are written with a templating language called handlebars, which has a bunch of dynamic helpers to insert your data into template files. Like , for example, outputs the name of the current author.

The best way to learn how to write your own Ghost theme is to have a look at the source code for Casper, which is heavily commented and should give you a sense of how everything fits together.

default.hbs is the main template file, all contexts will load inside this file unless specifically told to use a different template.

post.hbs is the file used in the context of viewing a post.

index.hbs is the file used in the context of viewing the home page.

and so on

We've got full and extensive theme documentation which outlines every template file, context and helper that you can use.

If you want to chat with other people making Ghost themes to get any advice or help, there's also a #themes channel in our public Slack community which we always recommend joining!

</description>

        <pubDate>Thu, 27 Jul 2017 06:00:00 +0200</pubDate>
        <link>
        /themes</link>
        <guid isPermaLink="true">/themes</guid>
      </item>

      <item>
        <title>The Businessman & The Fisherman</title>

          <description>
An American businessman took a vacation to a small coastal Mexican village on doctor's orders. Unable to sleep after an urgent phone call from the office the first morning, he walked out to the pier to clear his head. A small boat with just one fisherman had docked, and inside the boat were several large yellowfin tuna. The American complimented the Mexican on the quality of his fish.

</description>

        <pubDate>Tue, 12 Aug 2014 12:18:00 +0200</pubDate>
        <link>
        /the-businessman-and-fisherman</link>
        <guid isPermaLink="true">/the-businessman-and-fisherman</guid>
      </item>

      <item>
        <title>A Full and Comprehensive Style Test</title>

          <description>
Below is just about everything you'll need to style in the theme. Check the source code to see the many embedded elements within paragraphs.

</description>

        <pubDate>Sat, 01 Sep 2012 12:00:00 +0200</pubDate>
        <link>
        /a-full-and-comprehensive-style-test</link>
        <guid isPermaLink="true">/a-full-and-comprehensive-style-test</guid>
      </item>

      <item>
        <title>I Have a Dream</title>

          <description>
I am happy to join with you today in what will go down in history as the greatest demonstration for freedom in the history of our nation.

</description>

        <pubDate>Wed, 28 Aug 1963 11:18:00 +0100</pubDate>
        <link>
        /i-have-a-dream</link>
        <guid isPermaLink="true">/i-have-a-dream</guid>
      </item>

      <item>
        <title>The Purpose of Education</title>

          <description>
As I engage in the so-called "bull sessions" around and about the school, I too often find that most college men have a misconception of the purpose of education. Most of the "brethren" think that education should equip them with the proper instruments of exploitation so that they can forever trample over the masses. Still others think that education should furnish them with noble ends rather than means to an end.

</description>

        <pubDate>Sun, 12 Dec 1948 11:18:00 +0100</pubDate>
        <link>
        /the-purpose-of-education</link>
        <guid isPermaLink="true">/the-purpose-of-education</guid>
      </item>

      <item>
        <title>Out to Sea</title>

          <description>
I had this story from one who had no business to tell it to me, or to any other. I may credit the seductive influence of an old vintage upon the narrator for the beginning of it, and my own skeptical incredulity during the days that followed for the balance of the strange tale.

</description>

        <pubDate>Wed, 24 Jul 1912 11:18:00 +0100</pubDate>
        <link>
        /out-to-sea</link>
        <guid isPermaLink="true">/out-to-sea</guid>
      </item>

      <item>
        <title>Looking-Glass house</title>

          <description>
One thing was certain, that the white kitten had had nothing to do with it:— it was the black kitten's fault entirely. For the white kitten had been having its face washed by the old cat for the last quarter of an hour (and bearing it pretty well, considering); so you see that it couldn't have had any hand in the mischief.
The way Dinah washed her children's faces was this: first she held the poor thing down by its ear with one paw, and then with the other paw she rubbed its face all over, the wrong way, beginning at the nose: and just now, as I said, she was hard at work on the white kitten, which was lying quite still and trying to purr — no doubt feeling that it was all meant for its good.
But the black kitten had been finished with earlier in the afternoon, and so, while Alice was sitting curled up in a corner of the great arm-chair, half talking to herself and half asleep, the kitten had been having a grand game of romps with the ball of worsted Alice had been trying to wind up, and had been rolling it up and down till it had all come undone again; and there it was, spread over the hearth-rug, all knots and tangles, with the kitten running after its own tail in the middle.

</description>

        <pubDate>Sat, 18 Mar 1871 11:12:24 +0053</pubDate>
        <link>
        /looking-glass-house</link>
        <guid isPermaLink="true">/looking-glass-house</guid>
      </item>

      <item>
        <title>Down The Rabbit Hole</title>

          <description>
Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversation?'

</description>

        <pubDate>Sun, 26 Nov 1865 11:12:24 +0053</pubDate>
        <link>
        /down-the-rabbit-hole</link>
        <guid isPermaLink="true">/down-the-rabbit-hole</guid>
      </item>

      <item>
        <title>Gettysburg Address</title>

          <description>
Fourscore and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.

</description>

        <pubDate>Thu, 19 Nov 1863 11:12:24 +0053</pubDate>
        <link>
        /gettysburg-address</link>
        <guid isPermaLink="true">/gettysburg-address</guid>
      </item>

  </channel>
</rss>

redpajama_set_name: RedPajamaGithub