Skip to content

Commit 56bba01

Browse files
committed
[c++20] Fix incorrect assumptions in checks for comparison category types.
In the presence of modules, we can have multiple lookup results for the same entity, and we need to re-check for completeness each time we consider a type.
1 parent d694594 commit 56bba01

File tree

4 files changed

+86
-22
lines changed

4 files changed

+86
-22
lines changed

clang/lib/AST/ComparisonCategories.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
5959
// a new entry representing it.
6060
DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
6161
&Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
62-
if (Lookup.size() != 1 || !isa<VarDecl>(Lookup.front()))
62+
if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
6363
return nullptr;
6464
Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
6565
return &Objects.back();
@@ -70,7 +70,7 @@ static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
7070
if (!StdNS) {
7171
DeclContextLookupResult Lookup =
7272
Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
73-
if (Lookup.size() == 1)
73+
if (!Lookup.empty())
7474
StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
7575
}
7676
return StdNS;
@@ -81,7 +81,7 @@ static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
8181
ComparisonCategoryType Kind) {
8282
StringRef Name = ComparisonCategories::getCategoryString(Kind);
8383
DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
84-
if (Lookup.size() == 1)
84+
if (!Lookup.empty())
8585
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
8686
return RD;
8787
return nullptr;

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7501,8 +7501,7 @@ class DefaultedComparisonSynthesizer
75017501
VarDecl *EqualVD = S.Context.CompCategories.getInfoForType(StrongOrdering)
75027502
.getValueInfo(ComparisonCategoryResult::Equal)
75037503
->VD;
7504-
RetVal = S.BuildDeclarationNameExpr(
7505-
CXXScopeSpec(), DeclarationNameInfo(), EqualVD);
7504+
RetVal = getDecl(EqualVD);
75067505
if (RetVal.isInvalid())
75077506
return StmtError();
75087507
RetVal = buildStaticCastToR(RetVal.get());
@@ -7527,10 +7526,14 @@ class DefaultedComparisonSynthesizer
75277526
}
75287527

75297528
private:
7529+
ExprResult getDecl(ValueDecl *VD) {
7530+
return S.BuildDeclarationNameExpr(
7531+
CXXScopeSpec(), DeclarationNameInfo(VD->getDeclName(), Loc), VD);
7532+
}
7533+
75307534
ExprResult getParam(unsigned I) {
75317535
ParmVarDecl *PD = FD->getParamDecl(I);
7532-
return S.BuildDeclarationNameExpr(
7533-
CXXScopeSpec(), DeclarationNameInfo(PD->getDeclName(), Loc), PD);
7536+
return getDecl(PD);
75347537
}
75357538

75367539
ExprPair getCompleteObject() {
@@ -7622,8 +7625,7 @@ class DefaultedComparisonSynthesizer
76227625
Stmt *InitStmt = new (S.Context) DeclStmt(DeclGroupRef(VD), Loc, Loc);
76237626

76247627
// cmp != 0
7625-
ExprResult VDRef = S.BuildDeclarationNameExpr(
7626-
CXXScopeSpec(), DeclarationNameInfo(Name, Loc), VD);
7628+
ExprResult VDRef = getDecl(VD);
76277629
if (VDRef.isInvalid())
76287630
return StmtError();
76297631
llvm::APInt ZeroVal(S.Context.getIntWidth(S.Context.IntTy), 0);
@@ -7639,8 +7641,7 @@ class DefaultedComparisonSynthesizer
76397641
return StmtError();
76407642

76417643
// return cmp;
7642-
VDRef = S.BuildDeclarationNameExpr(
7643-
CXXScopeSpec(), DeclarationNameInfo(Name, Loc), VD);
7644+
VDRef = getDecl(VD);
76447645
if (VDRef.isInvalid())
76457646
return StmtError();
76467647
StmtResult ReturnStmt = S.BuildReturnStmt(Loc, VDRef.get());
@@ -10235,11 +10236,25 @@ QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind,
1023510236
assert(getLangOpts().CPlusPlus &&
1023610237
"Looking for comparison category type outside of C++.");
1023710238

10239+
// Use an elaborated type for diagnostics which has a name containing the
10240+
// prepended 'std' namespace but not any inline namespace names.
10241+
auto TyForDiags = [&](ComparisonCategoryInfo *Info) {
10242+
auto *NNS =
10243+
NestedNameSpecifier::Create(Context, nullptr, getStdNamespace());
10244+
return Context.getElaboratedType(ETK_None, NNS, Info->getType());
10245+
};
10246+
1023810247
// Check if we've already successfully checked the comparison category type
1023910248
// before. If so, skip checking it again.
1024010249
ComparisonCategoryInfo *Info = Context.CompCategories.lookupInfo(Kind);
10241-
if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)])
10250+
if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)]) {
10251+
// The only thing we need to check is that the type has a reachable
10252+
// definition in the current context.
10253+
if (RequireCompleteType(Loc, TyForDiags(Info), diag::err_incomplete_type))
10254+
return QualType();
10255+
1024210256
return Info->getType();
10257+
}
1024310258

1024410259
// If lookup failed
1024510260
if (!Info) {
@@ -10258,18 +10273,10 @@ QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind,
1025810273
if (Info->Record->hasDefinition())
1025910274
Info->Record = Info->Record->getDefinition();
1026010275

10261-
// Use an elaborated type for diagnostics which has a name containing the
10262-
// prepended 'std' namespace but not any inline namespace names.
10263-
QualType TyForDiags = [&]() {
10264-
auto *NNS =
10265-
NestedNameSpecifier::Create(Context, nullptr, getStdNamespace());
10266-
return Context.getElaboratedType(ETK_None, NNS, Info->getType());
10267-
}();
10268-
10269-
if (RequireCompleteType(Loc, TyForDiags, diag::err_incomplete_type))
10276+
if (RequireCompleteType(Loc, TyForDiags(Info), diag::err_incomplete_type))
1027010277
return QualType();
1027110278

10272-
InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags};
10279+
InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags(Info)};
1027310280

1027410281
if (!Info->Record->isTriviallyCopyable())
1027510282
return UnsupportedSTLError(USS_NonTrivial);

clang/test/SemaCXX/compare-cxx2a.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
55

6+
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -std=c++2a -x c++ %S/Inputs/std-compare.h -emit-pch -o %t.pch
7+
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s -include-pch %t.pch
8+
69
#include "Inputs/std-compare.h"
710

811
#define ASSERT_TYPE(...) static_assert(__is_same(__VA_ARGS__))
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -verify -std=c++2a -fmodules -I%S/Inputs %s -fno-modules-error-recovery
2+
3+
#pragma clang module build compare
4+
module compare {
5+
explicit module cmp {}
6+
explicit module other {}
7+
}
8+
#pragma clang module contents
9+
#pragma clang module begin compare.cmp
10+
#include "std-compare.h"
11+
#pragma clang module end
12+
#pragma clang module endbuild
13+
14+
struct CC { CC(...); };
15+
16+
void a() { void(0 <=> 0); } // expected-error {{include <compare>}}
17+
18+
struct A {
19+
CC operator<=>(const A&) const = default; // expected-error {{include <compare>}}
20+
};
21+
auto va = A() <=> A(); // expected-note {{required here}}
22+
23+
#pragma clang module import compare.other
24+
25+
// [email protected]:* 2+{{previous definition}}
26+
27+
void b() { void(0 <=> 0); } // expected-error 1+{{definition of 'strong_ordering' must be imported}}
28+
29+
struct B {
30+
CC operator<=>(const B&) const = default; // expected-error 1+{{definition of 'strong_ordering' must be imported}}
31+
};
32+
auto vb = B() <=> B(); // expected-note {{required here}}
33+
34+
#pragma clang module import compare.cmp
35+
36+
void c() { void(0 <=> 0); }
37+
38+
struct C {
39+
CC operator<=>(const C&) const = default;
40+
};
41+
auto vc = C() <=> C();
42+
43+
44+
#pragma clang module build compare2
45+
module compare2 {}
46+
#pragma clang module contents
47+
#pragma clang module begin compare2
48+
#include "std-compare.h"
49+
#pragma clang module end
50+
#pragma clang module endbuild
51+
52+
#pragma clang module import compare2
53+
54+
void g() { void(0.0 <=> 0.0); }

0 commit comments

Comments
 (0)