summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2019-12-05 13:37:35 -0800
committerRichard Smith <richard@metafoo.co.uk>2019-12-09 17:40:36 -0800
commit848934c67d484da737b4b92c087bffce069b24ba (patch)
tree85ddebae5f1249c32ef844f92e8477946c01f1cf /clang/lib
parent6507e13589687b40530dedc4dec670f2c1bfdc71 (diff)
downloadbcm5719-llvm-848934c67d484da737b4b92c087bffce069b24ba.tar.gz
bcm5719-llvm-848934c67d484da737b4b92c087bffce069b24ba.zip
[c++20] Fix handling of unqualified lookups from a defaulted comparison
function. We need to perform unqualified lookups from the context of a defaulted comparison, but not until we implicitly define the function, at which point we can't do those lookups any more. So perform the lookup from the end of the class containing the =default declaration and store the lookup results on the defaulted function until we synthesize the body.
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/Decl.cpp43
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp130
-rw-r--r--clang/lib/Sema/SemaLambda.cpp4
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp2
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp40
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp13
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp12
7 files changed, 197 insertions, 47 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 314d1750f43..9f09898a957 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2774,7 +2774,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
ConstexprSpecKind ConstexprKind)
: DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo,
StartLoc),
- DeclContext(DK), redeclarable_base(C), ODRHash(0),
+ DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
assert(T.isNull() || T->isFunctionType());
FunctionDeclBits.SClass = S;
@@ -2789,6 +2789,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
FunctionDeclBits.IsTrivialForCall = false;
FunctionDeclBits.IsDefaulted = false;
FunctionDeclBits.IsExplicitlyDefaulted = false;
+ FunctionDeclBits.HasDefaultedFunctionInfo = false;
FunctionDeclBits.HasImplicitReturnZero = false;
FunctionDeclBits.IsLateTemplateParsed = false;
FunctionDeclBits.ConstexprKind = ConstexprKind;
@@ -2816,6 +2817,32 @@ bool FunctionDecl::isVariadic() const {
return false;
}
+FunctionDecl::DefaultedFunctionInfo *
+FunctionDecl::DefaultedFunctionInfo::Create(ASTContext &Context,
+ ArrayRef<DeclAccessPair> Lookups) {
+ DefaultedFunctionInfo *Info = new (Context.Allocate(
+ totalSizeToAlloc<DeclAccessPair>(Lookups.size()),
+ std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair))))
+ DefaultedFunctionInfo;
+ Info->NumLookups = Lookups.size();
+ std::uninitialized_copy(Lookups.begin(), Lookups.end(),
+ Info->getTrailingObjects<DeclAccessPair>());
+ return Info;
+}
+
+void FunctionDecl::setDefaultedFunctionInfo(DefaultedFunctionInfo *Info) {
+ assert(!FunctionDeclBits.HasDefaultedFunctionInfo && "already have this");
+ assert(!Body && "can't replace function body with defaulted function info");
+
+ FunctionDeclBits.HasDefaultedFunctionInfo = true;
+ DefaultedInfo = Info;
+}
+
+FunctionDecl::DefaultedFunctionInfo *
+FunctionDecl::getDefaultedFunctionInfo() const {
+ return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr;
+}
+
bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
for (auto I : redecls()) {
if (I->doesThisDeclarationHaveABody()) {
@@ -2827,8 +2854,7 @@ bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
return false;
}
-bool FunctionDecl::hasTrivialBody() const
-{
+bool FunctionDecl::hasTrivialBody() const {
Stmt *S = getBody();
if (!S) {
// Since we don't have a body for this function, we don't know if it's
@@ -2856,6 +2882,8 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
if (!hasBody(Definition))
return nullptr;
+ assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo &&
+ "definition should not have a body");
if (Definition->Body)
return Definition->Body.get(getASTContext().getExternalSource());
@@ -2863,7 +2891,8 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
}
void FunctionDecl::setBody(Stmt *B) {
- Body = B;
+ FunctionDeclBits.HasDefaultedFunctionInfo = false;
+ Body = LazyDeclStmtPtr(B);
if (B)
EndRangeLoc = B->getEndLoc();
}
@@ -3304,9 +3333,9 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
const FunctionDecl *Prev = this;
bool FoundBody = false;
while ((Prev = Prev->getPreviousDecl())) {
- FoundBody |= Prev->Body.isValid();
+ FoundBody |= Prev->doesThisDeclarationHaveABody();
- if (Prev->Body) {
+ if (Prev->doesThisDeclarationHaveABody()) {
// If it's not the case that both 'inline' and 'extern' are
// specified on the definition, then it is always externally visible.
if (!Prev->isInlineSpecified() ||
@@ -3329,7 +3358,7 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
const FunctionDecl *Prev = this;
bool FoundBody = false;
while ((Prev = Prev->getPreviousDecl())) {
- FoundBody |= Prev->Body.isValid();
+ FoundBody |= Prev->doesThisDeclarationHaveABody();
if (RedeclForcesDefC99(Prev))
return false;
}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1ee6ee5dcf1..6d15e1e2025 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6296,7 +6296,11 @@ static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
/// Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
-void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
+///
+/// \param S The scope in which the class was parsed. Null if we didn't just
+/// parse a class definition.
+/// \param Record The completed class.
+void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
if (!Record)
return;
@@ -6412,13 +6416,17 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
DFK.asComparison() == DefaultedComparisonKind::Relational)
DefaultedSecondaryComparisons.push_back(FD);
else
- CheckExplicitlyDefaultedFunction(FD);
+ CheckExplicitlyDefaultedFunction(S, FD);
};
auto CompleteMemberFunction = [&](CXXMethodDecl *M) {
// Check whether the explicitly-defaulted members are valid.
CheckForDefaultedFunction(M);
+ // Skip the rest of the checks for a member of a dependent class.
+ if (Record->isDependentType())
+ return;
+
// For an explicitly defaulted or deleted special member, we defer
// determining triviality until the class is complete. That time is now!
CXXSpecialMember CSM = getSpecialMember(M);
@@ -6463,18 +6471,20 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
DefineImplicitSpecialMember(*this, M, M->getLocation());
};
+ // Check the destructor before any other member function. We need to
+ // determine whether it's trivial in order to determine whether the claas
+ // type is a literal type, which is a prerequisite for determining whether
+ // other special member functions are valid and whether they're implicitly
+ // 'constexpr'.
+ if (CXXDestructorDecl *Dtor = Record->getDestructor())
+ CompleteMemberFunction(Dtor);
+
bool HasMethodWithOverrideControl = false,
HasOverridingMethodWithoutOverrideControl = false;
- if (!Record->isDependentType()) {
- // Check the destructor before any other member function. We need to
- // determine whether it's trivial in order to determine whether the claas
- // type is a literal type, which is a prerequisite for determining whether
- // other special member functions are valid and whether they're implicitly
- // 'constexpr'.
- if (CXXDestructorDecl *Dtor = Record->getDestructor())
- CompleteMemberFunction(Dtor);
-
- for (auto *M : Record->methods()) {
+ for (auto *M : Record->methods()) {
+ // FIXME: We could do this check for dependent types with non-dependent
+ // bases.
+ if (!Record->isDependentType()) {
// See if a method overloads virtual methods in a base
// class without overriding any.
if (!M->isStatic())
@@ -6483,10 +6493,10 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
HasMethodWithOverrideControl = true;
else if (M->size_overridden_methods() > 0)
HasOverridingMethodWithoutOverrideControl = true;
-
- if (!isa<CXXDestructorDecl>(M))
- CompleteMemberFunction(M);
}
+
+ if (!isa<CXXDestructorDecl>(M))
+ CompleteMemberFunction(M);
}
if (HasMethodWithOverrideControl &&
@@ -6507,7 +6517,7 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
// Check the defaulted secondary comparisons after any other member functions.
for (FunctionDecl *FD : DefaultedSecondaryComparisons)
- CheckExplicitlyDefaultedFunction(FD);
+ CheckExplicitlyDefaultedFunction(S, FD);
// ms_struct is a request to use the same ABI rules as MSVC. Check
// whether this class uses any C++ features that are implemented
@@ -6862,16 +6872,19 @@ void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD)
UpdateExceptionSpec(MD->getCanonicalDecl(), ESI);
}
-void Sema::CheckExplicitlyDefaultedFunction(FunctionDecl *FD) {
+void Sema::CheckExplicitlyDefaultedFunction(Scope *S, FunctionDecl *FD) {
assert(FD->isExplicitlyDefaulted() && "not explicitly-defaulted");
DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD);
- assert(DefKind && "not a defaultable function");
+ if (!DefKind) {
+ assert(FD->getDeclContext()->isDependentContext());
+ return;
+ }
if (DefKind.isSpecialMember()
? CheckExplicitlyDefaultedSpecialMember(cast<CXXMethodDecl>(FD),
DefKind.asSpecialMember())
- : CheckExplicitlyDefaultedComparison(FD, DefKind.asComparison()))
+ : CheckExplicitlyDefaultedComparison(S, FD, DefKind.asComparison()))
FD->setInvalidDecl();
}
@@ -6882,6 +6895,10 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid &&
"not an explicitly-defaulted special member");
+ // Defer all checking for special members of a dependent type.
+ if (RD->isDependentType())
+ return false;
+
// Whether this was the first-declared instance of the constructor.
// This affects whether we implicitly add an exception spec and constexpr.
bool First = MD == MD->getCanonicalDecl();
@@ -7093,7 +7110,14 @@ public:
DefaultedComparisonVisitor(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD,
DefaultedComparisonKind DCK)
- : S(S), RD(RD), FD(FD), DCK(DCK) {}
+ : S(S), RD(RD), FD(FD), DCK(DCK) {
+ if (auto *Info = FD->getDefaultedFunctionInfo()) {
+ // FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an
+ // UnresolvedSet to avoid this copy.
+ Fns.assign(Info->getUnqualifiedLookups().begin(),
+ Info->getUnqualifiedLookups().end());
+ }
+ }
ResultList visit() {
// The type of an lvalue naming a parameter of this function.
@@ -7182,6 +7206,7 @@ protected:
CXXRecordDecl *RD;
FunctionDecl *FD;
DefaultedComparisonKind DCK;
+ UnresolvedSet<16> Fns;
};
/// Information about a defaulted comparison, as determined by
@@ -7289,8 +7314,6 @@ private:
visitBinaryOperator(OverloadedOperatorKind OO, ArrayRef<Expr *> Args,
Subobject Subobj,
OverloadCandidateSet *SpaceshipCandidates = nullptr) {
- UnresolvedSet<4> Fns; // FIXME: Track this.
-
// Note that there is no need to consider rewritten candidates here if
// we've already found there is no viable 'operator<=>' candidate (and are
// considering synthesizing a '<=>' from '==' and '<').
@@ -7318,9 +7341,11 @@ private:
if ((DCK == DefaultedComparisonKind::NotEqual ||
DCK == DefaultedComparisonKind::Relational) &&
!Best->RewriteKind) {
- S.Diag(Best->Function->getLocation(),
- diag::note_defaulted_comparison_not_rewritten_callee)
- << FD;
+ if (Diagnose == ExplainDeleted) {
+ S.Diag(Best->Function->getLocation(),
+ diag::note_defaulted_comparison_not_rewritten_callee)
+ << FD;
+ }
return Result::deleted();
}
@@ -7703,8 +7728,6 @@ private:
}
StmtResult visitExpandedSubobject(QualType Type, ExprPair Obj) {
- UnresolvedSet<4> Fns; // FIXME: Track this.
-
if (Obj.first.isInvalid() || Obj.second.isInvalid())
return StmtError();
@@ -7799,23 +7822,58 @@ private:
};
}
-bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
+/// Perform the unqualified lookups that might be needed to form a defaulted
+/// comparison function for the given operator.
+static void lookupOperatorsForDefaultedComparison(Sema &Self, Scope *S,
+ UnresolvedSetImpl &Operators,
+ OverloadedOperatorKind Op) {
+ auto Lookup = [&](OverloadedOperatorKind OO) {
+ Self.LookupOverloadedOperatorName(OO, S, QualType(), QualType(), Operators);
+ };
+
+ // Every defaulted operator looks up itself.
+ Lookup(Op);
+ // ... and the rewritten form of itself, if any.
+ if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(Op))
+ Lookup(ExtraOp);
+
+ // For 'operator<=>', we also form a 'cmp != 0' expression, and might
+ // synthesize a three-way comparison from '<' and '=='.
+ if (Op == OO_Spaceship) {
+ Lookup(OO_ExclaimEqual);
+ Lookup(OO_Less);
+ Lookup(OO_EqualEqual);
+ }
+}
+
+bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
DefaultedComparisonKind DCK) {
assert(DCK != DefaultedComparisonKind::None && "not a defaulted comparison");
+ CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext());
+ assert(RD && "defaulted comparison is not defaulted in a class");
+
+ // Perform any unqualified lookups we're going to need to default this
+ // function.
+ if (S) {
+ UnresolvedSet<32> Operators;
+ lookupOperatorsForDefaultedComparison(*this, S, Operators,
+ FD->getOverloadedOperator());
+ FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create(
+ Context, Operators.pairs()));
+ }
+
// C++2a [class.compare.default]p1:
// A defaulted comparison operator function for some class C shall be a
// non-template function declared in the member-specification of C that is
// -- a non-static const member of C having one parameter of type
// const C&, or
// -- a friend of C having two parameters of type const C&.
- CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext());
- assert(RD && "defaulted comparison is not defaulted in a class");
-
QualType ExpectedParmType =
Context.getLValueReferenceType(Context.getRecordType(RD).withConst());
for (const ParmVarDecl *Param : FD->parameters()) {
- if (!Context.hasSameType(Param->getType(), ExpectedParmType)) {
+ if (!Param->getType()->isDependentType() &&
+ !Context.hasSameType(Param->getType(), ExpectedParmType)) {
Diag(FD->getLocation(), diag::err_defaulted_comparison_param)
<< (int)DCK << Param->getType() << ExpectedParmType
<< Param->getSourceRange();
@@ -7849,6 +7907,7 @@ bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
// A [defaulted comparison other than <=>] shall have a declared return
// type bool.
if (DCK != DefaultedComparisonKind::ThreeWay &&
+ !FD->getDeclaredReturnType()->isDependentType() &&
!Context.hasSameType(FD->getDeclaredReturnType(), Context.BoolTy)) {
Diag(FD->getLocation(), diag::err_defaulted_comparison_return_type_not_bool)
<< (int)DCK << FD->getDeclaredReturnType() << Context.BoolTy
@@ -7856,6 +7915,11 @@ bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
return true;
}
+ // For a defaulted function in a dependent class, defer all remaining checks
+ // until instantiation.
+ if (RD->isDependentType())
+ return false;
+
// Determine whether the function should be defined as deleted.
DefaultedComparisonInfo Info =
DefaultedComparisonAnalyzer(*this, RD, FD, DCK).visit();
@@ -9206,7 +9270,7 @@ void Sema::ActOnFinishCXXMemberSpecification(
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList);
- CheckCompletedCXXClass(cast<CXXRecordDecl>(TagDecl));
+ CheckCompletedCXXClass(S, cast<CXXRecordDecl>(TagDecl));
}
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 14b443e9dac..d09a3377d2b 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1252,7 +1252,7 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,
SmallVector<Decl*, 4> Fields(Class->fields());
ActOnFields(nullptr, Class->getLocation(), Class, Fields, SourceLocation(),
SourceLocation(), ParsedAttributesView());
- CheckCompletedCXXClass(Class);
+ CheckCompletedCXXClass(nullptr, Class);
PopFunctionScopeInfo();
}
@@ -1798,7 +1798,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
SmallVector<Decl*, 4> Fields(Class->fields());
ActOnFields(nullptr, Class->getLocation(), Class, Fields, SourceLocation(),
SourceLocation(), ParsedAttributesView());
- CheckCompletedCXXClass(Class);
+ CheckCompletedCXXClass(nullptr, Class);
}
Cleanup.mergeFrom(LambdaCleanup);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 4d54ec17b99..1451fe4bb4d 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2236,7 +2236,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
// Finish checking fields.
ActOnFields(nullptr, Instantiation->getLocation(), Instantiation, Fields,
SourceLocation(), SourceLocation(), ParsedAttributesView());
- CheckCompletedCXXClass(Instantiation);
+ CheckCompletedCXXClass(nullptr, Instantiation);
// Default arguments are parsed, if not instantiated. We can go instantiate
// default arg exprs for default constructors if necessary now. Unless we're
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 2ab282e05e3..fb19579cca1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2077,8 +2077,10 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
}
}
- if (D->isExplicitlyDefaulted())
- SemaRef.SetDeclDefaulted(Function, D->getLocation());
+ if (D->isExplicitlyDefaulted()) {
+ if (SubstDefaultedFunction(Function, D))
+ return nullptr;
+ }
if (D->isDeleted())
SemaRef.SetDeclDeleted(Function, D->getLocation());
@@ -2357,8 +2359,10 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
SemaRef.CheckOverrideControl(Method);
// If a function is defined as defaulted or deleted, mark it as such now.
- if (D->isExplicitlyDefaulted())
- SemaRef.SetDeclDefaulted(Method, Method->getLocation());
+ if (D->isExplicitlyDefaulted()) {
+ if (SubstDefaultedFunction(Method, D))
+ return nullptr;
+ }
if (D->isDeletedAsWritten())
SemaRef.SetDeclDeleted(Method, Method->getLocation());
@@ -4139,6 +4143,34 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
return false;
}
+bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
+ FunctionDecl *Tmpl) {
+ // Transfer across any unqualified lookups.
+ if (auto *DFI = Tmpl->getDefaultedFunctionInfo()) {
+ SmallVector<DeclAccessPair, 32> Lookups;
+ Lookups.reserve(DFI->getUnqualifiedLookups().size());
+ bool AnyChanged = false;
+ for (DeclAccessPair DA : DFI->getUnqualifiedLookups()) {
+ NamedDecl *D = SemaRef.FindInstantiatedDecl(New->getLocation(),
+ DA.getDecl(), TemplateArgs);
+ if (!D)
+ return true;
+ AnyChanged |= (D != DA.getDecl());
+ Lookups.push_back(DeclAccessPair::make(D, DA.getAccess()));
+ }
+
+ // It's unlikely that substitution will change any declarations. Don't
+ // store an unnecessary copy in that case.
+ New->setDefaultedFunctionInfo(
+ AnyChanged ? FunctionDecl::DefaultedFunctionInfo::Create(
+ SemaRef.Context, Lookups)
+ : DFI);
+ }
+
+ SemaRef.SetDeclDefaulted(New, Tmpl->getLocation());
+ return false;
+}
+
/// Instantiate (or find existing instantiation of) a function template with a
/// given set of template arguments.
///
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 8990379069b..8b9cb967719 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -888,6 +888,19 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
FD->setHasODRHash(true);
FD->setUsesFPIntrin(Record.readInt());
+ if (FD->isDefaulted()) {
+ if (unsigned NumLookups = Record.readInt()) {
+ SmallVector<DeclAccessPair, 8> Lookups;
+ for (unsigned I = 0; I != NumLookups; ++I) {
+ NamedDecl *ND = Record.readDeclAs<NamedDecl>();
+ AccessSpecifier AS = (AccessSpecifier)Record.readInt();
+ Lookups.push_back(DeclAccessPair::make(ND, AS));
+ }
+ FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create(
+ Reader.getContext(), Lookups));
+ }
+ }
+
switch ((FunctionDecl::TemplatedKind)Record.readInt()) {
case FunctionDecl::TK_NonTemplate:
mergeRedeclarable(FD, Redecl);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index d9efc9770fd..10dfc4b841a 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -562,6 +562,18 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
Record.push_back(D->getODRHash());
Record.push_back(D->usesFPIntrin());
+ if (D->isDefaulted()) {
+ if (auto *FDI = D->getDefaultedFunctionInfo()) {
+ Record.push_back(FDI->getUnqualifiedLookups().size());
+ for (DeclAccessPair P : FDI->getUnqualifiedLookups()) {
+ Record.AddDeclRef(P.getDecl());
+ Record.push_back(P.getAccess());
+ }
+ } else {
+ Record.push_back(0);
+ }
+ }
+
Record.push_back(D->getTemplatedKind());
switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
OpenPOWER on IntegriCloud