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.
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContextAllocate.h"
+#include "clang/AST/DeclAccessPair.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExternalASTSource.h"
TK_DependentFunctionTemplateSpecialization
};
+ /// Stashed information about a defaulted function definition whose body has
+ /// not yet been lazily generated.
+ class DefaultedFunctionInfo final
+ : llvm::TrailingObjects<DefaultedFunctionInfo, DeclAccessPair> {
+ friend TrailingObjects;
+ unsigned NumLookups;
+
+ public:
+ static DefaultedFunctionInfo *Create(ASTContext &Context,
+ ArrayRef<DeclAccessPair> Lookups);
+ /// Get the unqualified lookup results that should be used in this
+ /// defaulted function definition.
+ ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
+ return {getTrailingObjects<DeclAccessPair>(), NumLookups};
+ }
+ };
+
private:
/// A new[]'d array of pointers to VarDecls for the formal
/// parameters of this function. This is null if a prototype or if there are
/// no formals.
ParmVarDecl **ParamInfo = nullptr;
- LazyDeclStmtPtr Body;
+ /// The active member of this union is determined by
+ /// FunctionDeclBits.HasDefaultedFunctionInfo.
+ union {
+ /// The body of the function.
+ LazyDeclStmtPtr Body;
+ /// Information about a future defaulted function definition.
+ DefaultedFunctionInfo *DefaultedInfo;
+ };
unsigned ODRHash;
/// parser reaches the definition, if called before, this function will return
/// `false`.
bool isThisDeclarationADefinition() const {
- return isDeletedAsWritten() || isDefaulted() || Body || hasSkippedBody() ||
- isLateTemplateParsed() || willHaveBody() || hasDefiningAttr();
+ return isDeletedAsWritten() || isDefaulted() ||
+ doesThisDeclarationHaveABody() || hasSkippedBody() ||
+ willHaveBody() || hasDefiningAttr();
}
/// Returns whether this specific declaration of the function has a body.
bool doesThisDeclarationHaveABody() const {
- return Body || isLateTemplateParsed();
+ return (!FunctionDeclBits.HasDefaultedFunctionInfo && Body) ||
+ isLateTemplateParsed();
}
void setBody(Stmt *B);
- void setLazyBody(uint64_t Offset) { Body = Offset; }
+ void setLazyBody(uint64_t Offset) {
+ FunctionDeclBits.HasDefaultedFunctionInfo = false;
+ Body = LazyDeclStmtPtr(Offset);
+ }
+
+ void setDefaultedFunctionInfo(DefaultedFunctionInfo *Info);
+ DefaultedFunctionInfo *getDefaultedFunctionInfo() const;
/// Whether this function is variadic.
bool isVariadic() const;
/// constructor or a destructor.
uint64_t IsTrivialForCall : 1;
- /// Used by CXXMethodDecl
uint64_t IsDefaulted : 1;
- /// Used by CXXMethodDecl
uint64_t IsExplicitlyDefaulted : 1;
+ uint64_t HasDefaultedFunctionInfo : 1;
uint64_t HasImplicitReturnZero : 1;
uint64_t IsLateTemplateParsed : 1;
};
/// Number of non-inherited bits in FunctionDeclBitfields.
- enum { NumFunctionDeclBits = 26 };
+ enum { NumFunctionDeclBits = 27 };
/// Stores the bits used by CXXConstructorDecl. If modified
/// NumCXXConstructorDeclBits and the accessor
/// exactly 64 bits and thus the width of NumCtorInitializers
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
- uint64_t NumCtorInitializers : 22;
+ uint64_t NumCtorInitializers : 21;
uint64_t IsInheritingConstructor : 1;
/// Whether this constructor has a trail-allocated explicit specifier.
const_iterator begin() const { return const_iterator(decls().begin()); }
const_iterator end() const { return const_iterator(decls().end()); }
+ ArrayRef<DeclAccessPair> pairs() const { return decls(); }
+
void addDecl(NamedDecl *D) {
addDecl(D, AS_none);
}
void append(iterator I, iterator E) { decls().append(I.I, E.I); }
+ template<typename Iter> void assign(Iter I, Iter E) { decls().assign(I, E); }
+
DeclAccessPair &operator[](unsigned I) { return decls()[I]; }
const DeclAccessPair &operator[](unsigned I) const { return decls()[I]; }
/// Add [[gsl::Pointer]] attributes for std:: types.
void inferGslPointerAttribute(TypedefNameDecl *TD);
- void CheckCompletedCXXClass(CXXRecordDecl *Record);
+ void CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record);
/// Check that the C++ class annoated with "trivial_abi" satisfies all the
/// conditions that are needed for the attribute to have an effect.
StorageClass &SC);
void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD);
- void CheckExplicitlyDefaultedFunction(FunctionDecl *MD);
+ void CheckExplicitlyDefaultedFunction(Scope *S, FunctionDecl *MD);
bool CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
CXXSpecialMember CSM);
void CheckDelayedMemberExceptionSpecs();
- bool CheckExplicitlyDefaultedComparison(FunctionDecl *MD,
+ bool CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *MD,
DefaultedComparisonKind DCK);
void DefineDefaultedComparison(SourceLocation Loc, FunctionDecl *FD,
DefaultedComparisonKind DCK);
bool InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl);
bool InitMethodInstantiation(CXXMethodDecl *New, CXXMethodDecl *Tmpl);
+ bool SubstDefaultedFunction(FunctionDecl *New, FunctionDecl *Tmpl);
+
TemplateParameterList *
SubstTemplateParams(TemplateParameterList *List);
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;
FunctionDeclBits.IsTrivialForCall = false;
FunctionDeclBits.IsDefaulted = false;
FunctionDeclBits.IsExplicitlyDefaulted = false;
+ FunctionDeclBits.HasDefaultedFunctionInfo = false;
FunctionDeclBits.HasImplicitReturnZero = false;
FunctionDeclBits.IsLateTemplateParsed = false;
FunctionDeclBits.ConstexprKind = ConstexprKind;
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()) {
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
if (!hasBody(Definition))
return nullptr;
+ assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo &&
+ "definition should not have a body");
if (Definition->Body)
return Definition->Body.get(getASTContext().getExternalSource());
}
void FunctionDecl::setBody(Stmt *B) {
- Body = B;
+ FunctionDeclBits.HasDefaultedFunctionInfo = false;
+ Body = LazyDeclStmtPtr(B);
if (B)
EndRangeLoc = B->getEndLoc();
}
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() ||
const FunctionDecl *Prev = this;
bool FoundBody = false;
while ((Prev = Prev->getPreviousDecl())) {
- FoundBody |= Prev->Body.isValid();
+ FoundBody |= Prev->doesThisDeclarationHaveABody();
if (RedeclForcesDefC99(Prev))
return false;
}
/// 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;
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);
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())
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 &&
// 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
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();
}
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();
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.
CXXRecordDecl *RD;
FunctionDecl *FD;
DefaultedComparisonKind DCK;
+ UnresolvedSet<16> Fns;
};
/// Information about a defaulted comparison, as determined by
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 '<').
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();
}
}
StmtResult visitExpandedSubobject(QualType Type, ExprPair Obj) {
- UnresolvedSet<4> Fns; // FIXME: Track this.
-
if (Obj.first.isInvalid() || Obj.second.isInvalid())
return StmtError();
};
}
-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();
// 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
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();
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList);
- CheckCompletedCXXClass(cast<CXXRecordDecl>(TagDecl));
+ CheckCompletedCXXClass(S, cast<CXXRecordDecl>(TagDecl));
}
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
SmallVector<Decl*, 4> Fields(Class->fields());
ActOnFields(nullptr, Class->getLocation(), Class, Fields, SourceLocation(),
SourceLocation(), ParsedAttributesView());
- CheckCompletedCXXClass(Class);
+ CheckCompletedCXXClass(nullptr, Class);
PopFunctionScopeInfo();
}
SmallVector<Decl*, 4> Fields(Class->fields());
ActOnFields(nullptr, Class->getLocation(), Class, Fields, SourceLocation(),
SourceLocation(), ParsedAttributesView());
- CheckCompletedCXXClass(Class);
+ CheckCompletedCXXClass(nullptr, Class);
}
Cleanup.mergeFrom(LambdaCleanup);
// 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
}
}
- 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());
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());
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.
///
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);
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:
struct Bad { using type = Dependent<Bad>&; };
template struct Dependent<Bad>; // expected-note {{in instantiation of}}
+
+
+namespace std {
+ struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+ };
+ constexpr strong_ordering strong_ordering::equal = {0};
+ constexpr strong_ordering strong_ordering::greater = {1};
+ constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+namespace LookupContext {
+ struct A {};
+
+ namespace N {
+ template <typename T> auto f() {
+ bool operator==(const T &, const T &);
+ bool operator<(const T &, const T &);
+ struct B {
+ T a;
+ std::strong_ordering operator<=>(const B &) const = default;
+ };
+ return B();
+ }
+
+ auto g() {
+ struct Cmp { Cmp(std::strong_ordering); };
+ Cmp operator<=>(const A&, const A&);
+ bool operator!=(const Cmp&, int);
+ struct B {
+ A a;
+ Cmp operator<=>(const B &) const = default;
+ };
+ return B();
+ }
+
+ auto h() {
+ struct B;
+ bool operator==(const B&, const B&);
+ bool operator!=(const B&, const B&); // expected-note 2{{best match}}
+ std::strong_ordering operator<=>(const B&, const B&);
+ bool operator<(const B&, const B&); // expected-note 2{{best match}}
+ bool operator<=(const B&, const B&); // expected-note 2{{best match}}
+ bool operator>(const B&, const B&); // expected-note 2{{best match}}
+ bool operator>=(const B&, const B&); // expected-note 2{{best match}}
+
+ struct B {
+ bool operator!=(const B&) const = default; // expected-warning {{implicitly deleted}} expected-note {{deleted here}}
+ bool operator<(const B&) const = default; // expected-warning {{implicitly deleted}} expected-note {{deleted here}}
+ bool operator<=(const B&) const = default; // expected-warning {{implicitly deleted}} expected-note {{deleted here}}
+ bool operator>(const B&) const = default; // expected-warning {{implicitly deleted}} expected-note {{deleted here}}
+ bool operator>=(const B&) const = default; // expected-warning {{implicitly deleted}} expected-note {{deleted here}}
+ };
+ return B();
+ }
+ }
+
+ namespace M {
+ bool operator==(const A &, const A &) = delete;
+ bool operator<(const A &, const A &) = delete;
+ bool cmp = N::f<A>() < N::f<A>();
+
+ void operator<=>(const A &, const A &) = delete;
+ auto cmp2 = N::g() <=> N::g();
+
+ void use_h() {
+ N::h() != N::h(); // expected-error {{implicitly deleted}}
+ N::h() < N::h(); // expected-error {{implicitly deleted}}
+ N::h() <= N::h(); // expected-error {{implicitly deleted}}
+ N::h() > N::h(); // expected-error {{implicitly deleted}}
+ N::h() >= N::h(); // expected-error {{implicitly deleted}}
+ }
+ }
+}
--- /dev/null
+// RxN: %clang_cc1 -std=c++2a -verify -Wno-defaulted-function-deleted -include %s %s
+//
+// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t.pch
+// RUN: %clang_cc1 -std=c++2a -include-pch %t.pch %s -verify
+
+// expected-no-diagnostics
+
+#ifndef INCLUDED
+#define INCLUDED
+
+namespace std {
+ struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+ };
+ constexpr strong_ordering strong_ordering::equal = {0};
+ constexpr strong_ordering strong_ordering::greater = {1};
+ constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+// Ensure that we can round-trip DefaultedFunctionInfo through an AST file.
+namespace LookupContext {
+ struct A {};
+
+ namespace N {
+ template <typename T> auto f() {
+ bool operator==(const T &, const T &);
+ bool operator<(const T &, const T &);
+ struct B {
+ T a;
+ std::strong_ordering operator<=>(const B &) const = default;
+ };
+ return B();
+ }
+ }
+}
+
+#else
+
+namespace LookupContext {
+ namespace M {
+ bool operator<=>(const A &, const A &) = delete;
+ bool operator==(const A &, const A &) = delete;
+ bool operator<(const A &, const A &) = delete;
+ bool cmp = N::f<A>() < N::f<A>();
+ }
+}
+
+#endif