12 changes: 7 additions & 5 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
} // anonymous namespace

llvm::Expected<std::unique_ptr<CompilerInstance>>
IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
IncrementalCompilerBuilder::create(std::string TT,
std::vector<const char *> &ClangArgv) {

// If we don't know ClangArgv0 or the address of main() at this point, try
// to guess it anyway (it's possible on some platforms).
Expand Down Expand Up @@ -162,8 +163,7 @@ IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);

driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0],
llvm::sys::getProcessTriple(), Diags);
driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], TT, Diags);
Driver.setCheckInputsExist(false); // the input comes from mem buffers
llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv);
std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF));
Expand All @@ -185,7 +185,8 @@ IncrementalCompilerBuilder::CreateCpp() {
Argv.push_back("-xc++");
Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());

return IncrementalCompilerBuilder::create(Argv);
std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();
return IncrementalCompilerBuilder::create(TT, Argv);
}

llvm::Expected<std::unique_ptr<CompilerInstance>>
Expand Down Expand Up @@ -213,7 +214,8 @@ IncrementalCompilerBuilder::createCuda(bool device) {

Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());

return IncrementalCompilerBuilder::create(Argv);
std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();
return IncrementalCompilerBuilder::create(TT, Argv);
}

llvm::Expected<std::unique_ptr<CompilerInstance>>
Expand Down
29 changes: 20 additions & 9 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,8 +1234,11 @@ void Parser::ParseAvailabilityAttribute(
}
IdentifierLoc *Platform = ParseIdentifierLoc();
if (const IdentifierInfo *const Ident = Platform->Ident) {
// Disallow xrOS for availability attributes.
if (Ident->getName().contains("xrOS") || Ident->getName().contains("xros"))
Diag(Platform->Loc, diag::warn_availability_unknown_platform) << Ident;
// Canonicalize platform name from "macosx" to "macos".
if (Ident->getName() == "macosx")
else if (Ident->getName() == "macosx")
Platform->Ident = PP.getIdentifierInfo("macos");
// Canonicalize platform name from "macosx_app_extension" to
// "macos_app_extension".
Expand Down Expand Up @@ -5678,24 +5681,32 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() {
// Parse a top-level-stmt.
Parser::StmtVector Stmts;
ParsedStmtContext SubStmtCtx = ParsedStmtContext();
Actions.PushFunctionScope();
ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
Scope::CompoundStmtScope);
TopLevelStmtDecl *TLSD = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
Actions.PopFunctionScopeInfo();
if (!R.isUsable())
return nullptr;

SmallVector<Decl *, 2> DeclsInGroup;
DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(R.get()));
Actions.ActOnFinishTopLevelStmtDecl(TLSD, R.get());

if (Tok.is(tok::annot_repl_input_end) &&
Tok.getAnnotationValue() != nullptr) {
ConsumeAnnotationToken();
cast<TopLevelStmtDecl>(DeclsInGroup.back())->setSemiMissing();
TLSD->setSemiMissing();
}

// Currently happens for things like -fms-extensions and use `__if_exists`.
for (Stmt *S : Stmts)
DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(S));
SmallVector<Decl *, 2> DeclsInGroup;
DeclsInGroup.push_back(TLSD);

// Currently happens for things like -fms-extensions and use `__if_exists`.
for (Stmt *S : Stmts) {
// Here we should be safe as `__if_exists` and friends are not introducing
// new variables which need to live outside file scope.
TopLevelStmtDecl *D = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
Actions.ActOnFinishTopLevelStmtDecl(D, S);
DeclsInGroup.push_back(D);
}

return Actions.BuildDeclaratorGroup(DeclsInGroup);
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3863,7 +3863,8 @@ std::optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() {
StringRef Platform =
AvailabilityAttr::canonicalizePlatformName(GivenPlatform);

if (AvailabilityAttr::getPrettyPlatformName(Platform).empty()) {
if (AvailabilityAttr::getPrettyPlatformName(Platform).empty() ||
(GivenPlatform.contains("xros") || GivenPlatform.contains("xrOS"))) {
Diag(PlatformIdentifier->Loc,
diag::err_avail_query_unrecognized_platform_name)
<< GivenPlatform;
Expand Down
25 changes: 23 additions & 2 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19184,8 +19184,24 @@ static bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) {
}

/// Check if two fields are layout-compatible.
/// Can be used on union members, which are exempt from alignment requirement
/// of common initial sequence.
static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
FieldDecl *Field2) {
FieldDecl *Field2,
bool AreUnionMembers = false) {
[[maybe_unused]] const Type *Field1Parent =
Field1->getParent()->getTypeForDecl();
[[maybe_unused]] const Type *Field2Parent =
Field2->getParent()->getTypeForDecl();
assert(((Field1Parent->isStructureOrClassType() &&
Field2Parent->isStructureOrClassType()) ||
(Field1Parent->isUnionType() && Field2Parent->isUnionType())) &&
"Can't evaluate layout compatibility between a struct field and a "
"union field.");
assert(((!AreUnionMembers && Field1Parent->isStructureOrClassType()) ||
(AreUnionMembers && Field1Parent->isUnionType())) &&
"AreUnionMembers should be 'true' for union fields (only).");

if (!isLayoutCompatible(C, Field1->getType(), Field2->getType()))
return false;

Expand All @@ -19204,6 +19220,11 @@ static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
if (Field1->hasAttr<clang::NoUniqueAddressAttr>() ||
Field2->hasAttr<clang::NoUniqueAddressAttr>())
return false;

if (!AreUnionMembers &&
Field1->getMaxAlignment() != Field2->getMaxAlignment())
return false;

return true;
}

Expand Down Expand Up @@ -19265,7 +19286,7 @@ static bool isLayoutCompatibleUnion(ASTContext &C, RecordDecl *RD1,
E = UnmatchedFields.end();

for ( ; I != E; ++I) {
if (isLayoutCompatible(C, Field1, *I)) {
if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
bool Result = UnmatchedFields.erase(*I);
(void) Result;
assert(Result);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,8 @@ bool Sema::addInstantiatedCapturesToScope(

bool Sema::SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
const MultiLevelTemplateArgumentList &MLTAL,
LocalInstantiationScope &Scope) {
if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
InstantiatingTemplate Inst(
Expand Down
43 changes: 29 additions & 14 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,9 +1110,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
// unqualified-id followed by a < and name lookup finds either one
// or more functions or finds nothing.
if (!IsFilteredTemplateName)
FilterAcceptableTemplateNames(Result,
/*AllowFunctionTemplates=*/true,
/*AllowDependent=*/true);
FilterAcceptableTemplateNames(Result);

bool IsFunctionTemplate;
bool IsVarTemplate;
Expand All @@ -1122,7 +1120,6 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
Template = Context.getOverloadedTemplateName(Result.begin(),
Result.end());
} else if (!Result.empty()) {
assert(!Result.isUnresolvableResult());
auto *TD = cast<TemplateDecl>(getAsTemplateNameDecl(
*Result.begin(), /*AllowFunctionTemplates=*/true,
/*AllowDependent=*/false));
Expand Down Expand Up @@ -15795,10 +15792,19 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
// captures during transformation of nested lambdas, it is necessary to
// have the LSI properly restored.
if (isGenericLambdaCallOperatorSpecialization(FD)) {
assert(inTemplateInstantiation() &&
"There should be an active template instantiation on the stack "
"when instantiating a generic lambda!");
RebuildLambdaScopeInfo(cast<CXXMethodDecl>(D));
// C++2c 7.5.5.2p17 A member of a closure type shall not be explicitly
// instantiated, explicitly specialized.
if (FD->getTemplateSpecializationInfo()
->isExplicitInstantiationOrSpecialization()) {
Diag(FD->getLocation(), diag::err_lambda_explicit_spec);
FD->setInvalidDecl();
PushFunctionScope();
} else {
assert(inTemplateInstantiation() &&
"There should be an active template instantiation on the stack "
"when instantiating a generic lambda!");
RebuildLambdaScopeInfo(cast<CXXMethodDecl>(D));
}
} else {
// Enter a new function scope
PushFunctionScope();
Expand Down Expand Up @@ -16317,9 +16323,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
}
}

assert(
(FD == getCurFunctionDecl() || getCurLambda()->CallOperator == FD) &&
"Function parsing confused");
assert((FD == getCurFunctionDecl(/*AllowLambdas=*/true)) &&
"Function parsing confused");
} else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) {
assert(MD == getCurMethodDecl() && "Method parsing confused");
MD->setBody(Body);
Expand Down Expand Up @@ -20519,12 +20524,22 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
return New;
}

Decl *Sema::ActOnTopLevelStmtDecl(Stmt *Statement) {
auto *New = TopLevelStmtDecl::Create(Context, Statement);
Context.getTranslationUnitDecl()->addDecl(New);
TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
auto *New = TopLevelStmtDecl::Create(Context, /*Statement=*/nullptr);
CurContext->addDecl(New);
PushDeclContext(S, New);
PushFunctionScope();
PushCompoundScope(false);
return New;
}

void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) {
D->setStmt(Statement);
PopCompoundScope();
PopFunctionScopeInfo();
PopDeclContext();
}

void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
IdentifierInfo* AliasName,
SourceLocation PragmaLoc,
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4877,7 +4877,9 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI,
NewElemTy = Context.getRealTypeForBitwidth(DestWidth, ExplicitType);

if (NewElemTy.isNull()) {
Diag(AttrLoc, diag::err_machine_mode) << 1 /*Unsupported*/ << Name;
// Only emit diagnostic on host for 128-bit mode attribute
if (!(DestWidth == 128 && getLangOpts().CUDAIsDevice))
Diag(AttrLoc, diag::err_machine_mode) << 1 /*Unsupported*/ << Name;
return;
}

Expand Down
93 changes: 52 additions & 41 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,8 @@ static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind,
static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
const CXXDestructorDecl *DD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) {
const CXXRecordDecl *RD =
T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
Expand Down Expand Up @@ -1746,6 +1748,8 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
static bool CheckConstexprParameterTypes(Sema &SemaRef,
const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
unsigned ArgIndex = 0;
const auto *FT = FD->getType()->castAs<FunctionProtoType>();
for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(),
Expand All @@ -1767,6 +1771,8 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
/// true. If not, produce a suitable diagnostic and return false.
static bool CheckConstexprReturnType(Sema &SemaRef, const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
diag::err_constexpr_non_literal_return,
FD->isConsteval()))
Expand Down Expand Up @@ -1856,25 +1862,28 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
}
}

// - its return type shall be a literal type;
if (!CheckConstexprReturnType(*this, NewFD, Kind))
// - its return type shall be a literal type; (removed in C++23)
if (!getLangOpts().CPlusPlus23 &&
!CheckConstexprReturnType(*this, NewFD, Kind))
return false;
}

if (auto *Dtor = dyn_cast<CXXDestructorDecl>(NewFD)) {
// A destructor can be constexpr only if the defaulted destructor could be;
// we don't need to check the members and bases if we already know they all
// have constexpr destructors.
if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
// have constexpr destructors. (removed in C++23)
if (!getLangOpts().CPlusPlus23 &&
!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
if (Kind == CheckConstexprKind::CheckValid)
return false;
if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind))
return false;
}
}

// - each of its parameter types shall be a literal type;
if (!CheckConstexprParameterTypes(*this, NewFD, Kind))
// - each of its parameter types shall be a literal type; (removed in C++23)
if (!getLangOpts().CPlusPlus23 &&
!CheckConstexprParameterTypes(*this, NewFD, Kind))
return false;

Stmt *Body = NewFD->getBody();
Expand Down Expand Up @@ -2457,7 +2466,8 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
// function", so is not checked in CheckValid mode.
SmallVector<PartialDiagnosticAt, 8> Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
!Expr::isPotentialConstantExpr(Dcl, Diags) &&
!SemaRef.getLangOpts().CPlusPlus23) {
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
Expand Down Expand Up @@ -7535,21 +7545,23 @@ static bool defaultedSpecialMemberIsConstexpr(

// C++1y [class.copy]p26:
// -- [the class] is a literal type, and
if (!Ctor && !ClassDecl->isLiteral())
if (!Ctor && !ClassDecl->isLiteral() && !S.getLangOpts().CPlusPlus23)
return false;

// -- every constructor involved in initializing [...] base class
// sub-objects shall be a constexpr constructor;
// -- the assignment operator selected to copy/move each direct base
// class is a constexpr function, and
for (const auto &B : ClassDecl->bases()) {
const RecordType *BaseType = B.getType()->getAs<RecordType>();
if (!BaseType)
continue;
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
InheritedCtor, Inherited))
return false;
if (!S.getLangOpts().CPlusPlus23) {
for (const auto &B : ClassDecl->bases()) {
const RecordType *BaseType = B.getType()->getAs<RecordType>();
if (!BaseType)
continue;
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
InheritedCtor, Inherited))
return false;
}
}

// -- every constructor involved in initializing non-static data members
Expand All @@ -7559,20 +7571,22 @@ static bool defaultedSpecialMemberIsConstexpr(
// -- for each non-static data member of X that is of class type (or array
// thereof), the assignment operator selected to copy/move that member is
// a constexpr function
for (const auto *F : ClassDecl->fields()) {
if (F->isInvalidDecl())
continue;
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
continue;
QualType BaseType = S.Context.getBaseElementType(F->getType());
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
BaseType.getCVRQualifiers(),
ConstArg && !F->isMutable()))
if (!S.getLangOpts().CPlusPlus23) {
for (const auto *F : ClassDecl->fields()) {
if (F->isInvalidDecl())
continue;
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
continue;
QualType BaseType = S.Context.getBaseElementType(F->getType());
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
BaseType.getCVRQualifiers(),
ConstArg && !F->isMutable()))
return false;
} else if (CSM == Sema::CXXDefaultConstructor) {
return false;
} else if (CSM == Sema::CXXDefaultConstructor) {
return false;
}
}
}

Expand Down Expand Up @@ -7858,18 +7872,17 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
MD->isConstexpr() && !Constexpr &&
MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
if (!MD->isConsteval() && RD->getNumVBases()) {
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr_with_vb)
Diag(MD->getBeginLoc(),
diag::err_incorrect_defaulted_constexpr_with_vb)
<< CSM;
for (const auto &I : RD->vbases())
Diag(I.getBeginLoc(), diag::note_constexpr_virtual_base_here);
} else {
Diag(MD->getBeginLoc(), MD->isConsteval()
? diag::err_incorrect_defaulted_consteval
: diag::err_incorrect_defaulted_constexpr)
<< CSM;
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr)
<< CSM << MD->isConsteval();
}
// FIXME: Explain why the special member can't be constexpr.
HadError = true;
HadError = true;
// FIXME: Explain why the special member can't be constexpr.
}

if (First) {
Expand Down Expand Up @@ -9101,13 +9114,11 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
// - if the function is a constructor or destructor, its class does not
// have any virtual base classes.
if (FD->isConstexpr()) {
if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
if (!getLangOpts().CPlusPlus23 &&
CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
!Info.Constexpr) {
Diag(FD->getBeginLoc(),
getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_defaulted_comparison_constexpr_mismatch
: diag::ext_defaulted_comparison_constexpr_mismatch)
Diag(FD->getBeginLoc(), diag::err_defaulted_comparison_constexpr_mismatch)
<< FD->isImplicit() << (int)DCK << FD->isConsteval();
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainConstexpr)
Expand Down
37 changes: 32 additions & 5 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10720,13 +10720,40 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (TemplateName.isDependent())
return SubstAutoTypeDependent(TSInfo->getType());

// We can only perform deduction for class templates.
// We can only perform deduction for class templates or alias templates.
auto *Template =
dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl());
TemplateDecl *LookupTemplateDecl = Template;
if (!Template) {
if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>(
TemplateName.getAsTemplateDecl())) {
Diag(Kind.getLocation(),
diag::warn_cxx17_compat_ctad_for_alias_templates);
LookupTemplateDecl = AliasTemplate;
auto UnderlyingType = AliasTemplate->getTemplatedDecl()
->getUnderlyingType()
.getCanonicalType();
// C++ [over.match.class.deduct#3]: ..., the defining-type-id of A must be
// of the form
// [typename] [nested-name-specifier] [template] simple-template-id
if (const auto *TST =
UnderlyingType->getAs<TemplateSpecializationType>()) {
Template = dyn_cast_or_null<ClassTemplateDecl>(
TST->getTemplateName().getAsTemplateDecl());
} else if (const auto *RT = UnderlyingType->getAs<RecordType>()) {
// Cases where template arguments in the RHS of the alias are not
// dependent. e.g.
// using AliasFoo = Foo<bool>;
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(
RT->getAsCXXRecordDecl()))
Template = CTSD->getSpecializedTemplate();
}
}
}
if (!Template) {
Diag(Kind.getLocation(),
diag::err_deduced_non_class_template_specialization_type)
<< (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName;
diag::err_deduced_non_class_or_alias_template_specialization_type)
<< (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName;
if (auto *TD = TemplateName.getAsTemplateDecl())
NoteTemplateLocation(*TD);
return QualType();
Expand All @@ -10753,10 +10780,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// template-name, a function template [...]
// - For each deduction-guide, a function or function template [...]
DeclarationNameInfo NameInfo(
Context.DeclarationNames.getCXXDeductionGuideName(Template),
Context.DeclarationNames.getCXXDeductionGuideName(LookupTemplateDecl),
TSInfo->getTypeLoc().getEndLoc());
LookupResult Guides(*this, NameInfo, LookupOrdinaryName);
LookupQualifiedName(Guides, Template->getDeclContext());
LookupQualifiedName(Guides, LookupTemplateDecl->getDeclContext());

// FIXME: Do not diagnose inaccessible deduction guides. The standard isn't
// clear on this, but they're not found by name so access does not apply.
Expand Down
508 changes: 418 additions & 90 deletions clang/lib/Sema/SemaTemplate.cpp

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
}

TemplateDeductionResult Sema::DeduceTemplateArguments(
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool NumberOfArgumentsMustMatch) {
return ::DeduceTemplateArguments(*this, TemplateParams, Ps, As, Info, Deduced,
NumberOfArgumentsMustMatch);
}

/// Determine whether two template arguments are the same.
static bool isSameTemplateArg(ASTContext &Context,
TemplateArgument X,
Expand Down
69 changes: 66 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Stack.h"
Expand Down Expand Up @@ -547,9 +548,9 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
: InstantiatingTemplate(SemaRef, Kind, PointOfInstantiation,
InstantiationRange, FunctionTemplate, nullptr,
TemplateArgs, &DeductionInfo) {
assert(
Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution ||
Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution);
assert(Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution ||
Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution ||
Kind == CodeSynthesisContext::BuildingDeductionGuides);
}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Expand Down Expand Up @@ -1446,6 +1447,59 @@ namespace {
return inherited::TransformFunctionProtoType(TLB, TL);
}

QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
InjectedClassNameTypeLoc TL) {
auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
// Special case for transforming a deduction guide, we return a
// transformed TemplateSpecializationType.
if (Type.isNull() &&
SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides) {
// Return a TemplateSpecializationType for transforming a deduction
// guide.
if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
auto Type =
inherited::TransformType(ICT->getInjectedSpecializationType());
TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
return Type;
}
}
return Type;
}
// Override the default version to handle a rewrite-template-arg-pack case
// for building a deduction guide.
bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
TemplateArgumentLoc &Output,
bool Uneval = false) {
const TemplateArgument &Arg = Input.getArgument();
std::vector<TemplateArgument> TArgs;
switch (Arg.getKind()) {
case TemplateArgument::Pack:
// Literally rewrite the template argument pack, instead of unpacking
// it.
assert(
SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides &&
"Transforming a template argument pack is only allowed in building "
"deduction guide");
for (auto &pack : Arg.getPackAsArray()) {
TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
pack, QualType(), SourceLocation{});
TemplateArgumentLoc Output;
if (SemaRef.SubstTemplateArgument(Input, TemplateArgs, Output))
return true; // fails
TArgs.push_back(Output.getArgument());
}
Output = SemaRef.getTrivialTemplateArgumentLoc(
TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
QualType(), SourceLocation{});
return false;
default:
break;
}
return inherited::TransformTemplateArgument(Input, Output, Uneval);
}

template<typename Fn>
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
FunctionProtoTypeLoc TL,
Expand Down Expand Up @@ -4138,6 +4192,15 @@ Sema::SubstStmt(Stmt *S, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformStmt(S);
}

bool Sema::SubstTemplateArgument(
const TemplateArgumentLoc &Input,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentLoc &Output) {
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
DeclarationName());
return Instantiator.TransformTemplateArgument(Input, Output);
}

bool Sema::SubstTemplateArguments(
ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
Expand Down
29 changes: 19 additions & 10 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2219,7 +2219,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
FunctionTemplate->setInstantiatedFromMemberTemplate(
D->getDescribedFunctionTemplate());
}
} else if (FunctionTemplate) {
} else if (FunctionTemplate &&
SemaRef.CodeSynthesisContexts.back().Kind !=
Sema::CodeSynthesisContext::BuildingDeductionGuides) {
// Record this function template specialization.
ArrayRef<TemplateArgument> Innermost = TemplateArgs.getInnermost();
Function->setFunctionTemplateSpecialization(FunctionTemplate,
Expand Down Expand Up @@ -4853,16 +4855,13 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
///
/// Usually this should not be used, and template argument deduction should be
/// used in its place.
FunctionDecl *
Sema::InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
const TemplateArgumentList *Args,
SourceLocation Loc) {
FunctionDecl *Sema::InstantiateFunctionDeclaration(
FunctionTemplateDecl *FTD, const TemplateArgumentList *Args,
SourceLocation Loc, CodeSynthesisContext::SynthesisKind CSC) {
FunctionDecl *FD = FTD->getTemplatedDecl();

sema::TemplateDeductionInfo Info(Loc);
InstantiatingTemplate Inst(
*this, Loc, FTD, Args->asArray(),
CodeSynthesisContext::ExplicitTemplateArgumentSubstitution, Info);
InstantiatingTemplate Inst(*this, Loc, FTD, Args->asArray(), CSC, Info);
if (Inst.isInvalid())
return nullptr;

Expand Down Expand Up @@ -6286,8 +6285,18 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
QualType T = CheckTemplateIdType(TemplateName(TD), Loc, Args);
if (T.isNull())
return nullptr;
auto *SubstRecord = T->getAsCXXRecordDecl();
assert(SubstRecord && "class template id not a class type?");
CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl();

if (!SubstRecord) {
// T can be a dependent TemplateSpecializationType when performing a
// substitution for building a deduction guide.
assert(CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// Return a nullptr as a sentinel value, we handle it properly in
// the TemplateInstantiator::TransformInjectedClassNameType
// override, which we transform it to a TemplateSpecializationType.
return nullptr;
}
// Check that this template-id names the primary template and not a
// partial or explicit specialization. (In the latter cases, it's
// meaningless to attempt to find an instantiation of D within the
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
break;
case DeclSpec::TST_float128:
if (!S.Context.getTargetInfo().hasFloat128Type() &&
!S.getLangOpts().SYCLIsDevice &&
!S.getLangOpts().SYCLIsDevice && !S.getLangOpts().CUDAIsDevice &&
!(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsTargetDevice))
S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
<< "__float128";
Expand Down
31 changes: 29 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -4785,6 +4785,14 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
TemplateArgumentLoc In = *First;

if (In.getArgument().getKind() == TemplateArgument::Pack) {
// When building the deduction guides, we rewrite the argument packs
// instead of unpacking.
if (getSema().CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides) {
if (getDerived().TransformTemplateArgument(In, Out, Uneval))
return true;
continue;
}
// Unpack argument packs, which we translate them into separate
// arguments.
// FIXME: We could do much better if we could guarantee that the
Expand Down Expand Up @@ -13649,10 +13657,29 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// use evaluation contexts to distinguish the function parameter case.
CXXRecordDecl::LambdaDependencyKind DependencyKind =
CXXRecordDecl::LDK_Unknown;
DeclContext *DC = getSema().CurContext;
// A RequiresExprBodyDecl is not interesting for dependencies.
// For the following case,
//
// template <typename>
// concept C = requires { [] {}; };
//
// template <class F>
// struct Widget;
//
// template <C F>
// struct Widget<F> {};
//
// While we are substituting Widget<F>, the parent of DC would be
// the template specialization itself. Thus, the lambda expression
// will be deemed as dependent even if there are no dependent template
// arguments.
// (A ClassTemplateSpecializationDecl is always a dependent context.)
while (DC->getDeclKind() == Decl::Kind::RequiresExprBody)
DC = DC->getParent();
if ((getSema().isUnevaluatedContext() ||
getSema().isConstantEvaluatedContext()) &&
(getSema().CurContext->isFileContext() ||
!getSema().CurContext->getParent()->isDependentContext()))
(DC->isFileContext() || !DC->getParent()->isDependentContext()))
DependencyKind = CXXRecordDecl::LDK_NeverDependent;

CXXRecordDecl *OldClass = E->getLambdaClass();
Expand Down
32 changes: 18 additions & 14 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4623,10 +4623,12 @@ ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream,
SmallVectorImpl<char> &Buffer,
InMemoryModuleCache &ModuleCache,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool IncludeTimestamps, bool BuildingImplicitModule)
bool IncludeTimestamps, bool BuildingImplicitModule,
bool GeneratingReducedBMI)
: Stream(Stream), Buffer(Buffer), ModuleCache(ModuleCache),
IncludeTimestamps(IncludeTimestamps),
BuildingImplicitModule(BuildingImplicitModule) {
BuildingImplicitModule(BuildingImplicitModule),
GeneratingReducedBMI(GeneratingReducedBMI) {
for (const auto &Ext : Extensions) {
if (auto Writer = Ext->createExtensionWriter(*this))
ModuleFileExtensionWriters.push_back(std::move(Writer));
Expand Down Expand Up @@ -5457,18 +5459,20 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {

// Add a trailing update record, if any. These must go last because we
// lazily load their attached statement.
if (HasUpdatedBody) {
const auto *Def = cast<FunctionDecl>(D);
Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
Record.push_back(Def->isInlined());
Record.AddSourceLocation(Def->getInnerLocStart());
Record.AddFunctionDefinition(Def);
} else if (HasAddedVarDefinition) {
const auto *VD = cast<VarDecl>(D);
Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
Record.push_back(VD->isInline());
Record.push_back(VD->isInlineSpecified());
Record.AddVarDeclInit(VD);
if (!GeneratingReducedBMI || !CanElideDeclDef(D)) {
if (HasUpdatedBody) {
const auto *Def = cast<FunctionDecl>(D);
Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
Record.push_back(Def->isInlined());
Record.AddSourceLocation(Def->getInnerLocStart());
Record.AddFunctionDefinition(Def);
} else if (HasAddedVarDefinition) {
const auto *VD = cast<VarDecl>(D);
Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
Record.push_back(VD->isInline());
Record.push_back(VD->isInlineSpecified());
Record.AddVarDeclInit(VD);
}
}

OffsetsRecord.push_back(GetDeclRef(D));
Expand Down
45 changes: 38 additions & 7 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/OpenMPClause.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/Basic/SourceManager.h"
Expand All @@ -40,11 +41,14 @@ namespace clang {
serialization::DeclCode Code;
unsigned AbbrevToUse;

bool GeneratingReducedBMI = false;

public:
ASTDeclWriter(ASTWriter &Writer, ASTContext &Context,
ASTWriter::RecordDataImpl &Record)
ASTWriter::RecordDataImpl &Record, bool GeneratingReducedBMI)
: Writer(Writer), Context(Context), Record(Writer, Record),
Code((serialization::DeclCode)0), AbbrevToUse(0) {}
Code((serialization::DeclCode)0), AbbrevToUse(0),
GeneratingReducedBMI(GeneratingReducedBMI) {}

uint64_t Emit(Decl *D) {
if (!Code)
Expand Down Expand Up @@ -270,6 +274,27 @@ namespace clang {
};
}

bool clang::CanElideDeclDef(const Decl *D) {
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isInlined() || FD->isConstexpr())
return false;

if (FD->isDependentContext())
return false;
}

if (auto *VD = dyn_cast<VarDecl>(D)) {
if (!VD->getDeclContext()->getRedeclContext()->isFileContext() ||
VD->isInline() || VD->isConstexpr() || isa<ParmVarDecl>(VD))
return false;

if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
return false;
}

return true;
}

void ASTDeclWriter::Visit(Decl *D) {
DeclVisitor<ASTDeclWriter>::Visit(D);

Expand All @@ -285,17 +310,23 @@ void ASTDeclWriter::Visit(Decl *D) {
// have been written. We want it last because we will not read it back when
// retrieving it from the AST, we'll just lazily set the offset.
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
Record.push_back(FD->doesThisDeclarationHaveABody());
if (FD->doesThisDeclarationHaveABody())
Record.AddFunctionDefinition(FD);
if (!GeneratingReducedBMI || !CanElideDeclDef(FD)) {
Record.push_back(FD->doesThisDeclarationHaveABody());
if (FD->doesThisDeclarationHaveABody())
Record.AddFunctionDefinition(FD);
} else
Record.push_back(0);
}

// Similar to FunctionDecls, handle VarDecl's initializer here and write it
// after all other Stmts/Exprs. We will not read the initializer until after
// we have finished recursive deserialization, because it can recursively
// refer back to the variable.
if (auto *VD = dyn_cast<VarDecl>(D)) {
Record.AddVarDeclInit(VD);
if (!GeneratingReducedBMI || !CanElideDeclDef(VD))
Record.AddVarDeclInit(VD);
else
Record.push_back(0);
}

// And similarly for FieldDecls. We already serialized whether there is a
Expand Down Expand Up @@ -2729,7 +2760,7 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
assert(ID >= FirstDeclID && "invalid decl ID");

RecordData Record;
ASTDeclWriter W(*this, Context, Record);
ASTDeclWriter W(*this, Context, Record, GeneratingReducedBMI);

// Build a record for this declaration
W.Visit(D);
Expand Down
37 changes: 35 additions & 2 deletions clang/lib/Serialization/GeneratePCH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTContext.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/ASTWriter.h"
#include "llvm/Bitstream/BitstreamWriter.h"

Expand All @@ -25,11 +27,12 @@ PCHGenerator::PCHGenerator(
StringRef OutputFile, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool AllowASTWithErrors, bool IncludeTimestamps,
bool BuildingImplicitModule, bool ShouldCacheASTInMemory)
bool BuildingImplicitModule, bool ShouldCacheASTInMemory,
bool GeneratingReducedBMI)
: PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()),
SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data),
Writer(Stream, this->Buffer->Data, ModuleCache, Extensions,
IncludeTimestamps, BuildingImplicitModule),
IncludeTimestamps, BuildingImplicitModule, GeneratingReducedBMI),
AllowASTWithErrors(AllowASTWithErrors),
ShouldCacheASTInMemory(ShouldCacheASTInMemory) {
this->Buffer->IsComplete = false;
Expand Down Expand Up @@ -78,3 +81,33 @@ ASTMutationListener *PCHGenerator::GetASTMutationListener() {
ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() {
return &Writer;
}

ReducedBMIGenerator::ReducedBMIGenerator(const Preprocessor &PP,
InMemoryModuleCache &ModuleCache,
StringRef OutputFile,
std::shared_ptr<PCHBuffer> Buffer,
bool IncludeTimestamps)
: PCHGenerator(
PP, ModuleCache, OutputFile, llvm::StringRef(), Buffer,
/*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
/*AllowASTWithErrors*/ false, /*IncludeTimestamps=*/IncludeTimestamps,
/*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false,
/*GeneratingReducedBMI=*/true) {}

void ReducedBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) {
PCHGenerator::HandleTranslationUnit(Ctx);

if (!isComplete())
return;

std::error_code EC;
auto OS = std::make_unique<llvm::raw_fd_ostream>(getOutputFile(), EC);
if (EC) {
getDiagnostics().Report(diag::err_fe_unable_to_open_output)
<< getOutputFile() << EC.message() << "\n";
return;
}

*OS << getBufferPtr()->Data;
OS->flush();
}
91 changes: 57 additions & 34 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,64 +307,64 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{{{"fclose"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
{{{"fread"}, 4},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
{{{"fwrite"}, 4},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
{{{"fgetc"}, 1},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
{{{"fgets"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
{{{"getc"}, 1},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
{{{"fputc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
{{{"fputs"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
{{{"putc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
{{{"fprintf"}},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
{{{"vfprintf"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
{{{"fscanf"}},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
{{{"vfscanf"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
{{{"ungetc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
{&StreamChecker::preWrite,
std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
{{{"getdelim"}, 4},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
{{{"getline"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
{&StreamChecker::preRead,
std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
{{{"fseek"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"fseeko"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
{{{"ftello"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
{{{"fflush"}, 1},
{&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
{{{"rewind"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
{{{"fgetpos"}, 2},
{&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
{&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
{{{"fsetpos"}, 2},
{&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
{{{"clearerr"}, 1},
Expand All @@ -384,12 +384,18 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
CallDescriptionMap<FnDescription> FnTestDescriptions = {
{{{"StreamTesterChecker_make_feof_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
false),
0}},
{{{"StreamTesterChecker_make_ferror_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError),
ErrorFError, false),
0}},
{{{"StreamTesterChecker_make_ferror_indeterminate_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError, true),
0}},
};

Expand All @@ -415,8 +421,11 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
void evalFclose(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;

void preReadWrite(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C, bool IsRead) const;
void preRead(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;

void preWrite(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;

void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C, bool IsFread) const;
Expand Down Expand Up @@ -467,8 +476,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
const StreamErrorState &ErrorKind) const;

void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C,
const StreamErrorState &ErrorKind) const;
CheckerContext &C, const StreamErrorState &ErrorKind,
bool Indeterminate) const;

void preFflush(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
Expand Down Expand Up @@ -849,9 +858,8 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
C.addTransition(E.bindReturnValue(State, C, *EofVal));
}

void StreamChecker::preReadWrite(const FnDescription *Desc,
const CallEvent &Call, CheckerContext &C,
bool IsRead) const {
void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SVal StreamVal = getStreamArg(Desc, Call);
State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
Expand All @@ -865,11 +873,6 @@ void StreamChecker::preReadWrite(const FnDescription *Desc,
if (!State)
return;

if (!IsRead) {
C.addTransition(State);
return;
}

SymbolRef Sym = StreamVal.getAsSymbol();
if (Sym && State->get<StreamMap>(Sym)) {
const StreamState *SS = State->get<StreamMap>(Sym);
Expand All @@ -880,6 +883,24 @@ void StreamChecker::preReadWrite(const FnDescription *Desc,
}
}

void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
SVal StreamVal = getStreamArg(Desc, Call);
State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
State);
if (!State)
return;
State = ensureStreamOpened(StreamVal, C, State);
if (!State)
return;
State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
if (!State)
return;

C.addTransition(State);
}

void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
const CallEvent &Call, CheckerContext &C,
bool IsFread) const {
Expand Down Expand Up @@ -1496,14 +1517,16 @@ void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,

void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
const CallEvent &Call, CheckerContext &C,
const StreamErrorState &ErrorKind) const {
const StreamErrorState &ErrorKind,
bool Indeterminate) const {
ProgramStateRef State = C.getState();
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
const StreamState *SS = State->get<StreamMap>(StreamSym);
assert(SS && "Stream should be tracked by the checker.");
State = State->set<StreamMap>(
StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
StreamSym,
StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
C.addTransition(State);
}

Expand Down
76 changes: 55 additions & 21 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,19 @@ class TrivialFunctionAnalysisVisitor
return true;
}

template <typename CheckFunction>
bool WithCachedResult(const Stmt *S, CheckFunction Function) {
// If the statement isn't in the cache, conservatively assume that
// it's not trivial until analysis completes. Insert false to the cache
// first to avoid infinite recursion.
auto [It, IsNew] = Cache.insert(std::make_pair(S, false));
if (!IsNew)
return It->second;
bool Result = Function();
Cache[S] = Result;
return Result;
}

public:
using CacheTy = TrivialFunctionAnalysis::CacheTy;

Expand All @@ -267,7 +280,7 @@ class TrivialFunctionAnalysisVisitor
bool VisitCompoundStmt(const CompoundStmt *CS) {
// A compound statement is allowed as long each individual sub-statement
// is trivial.
return VisitChildren(CS);
return WithCachedResult(CS, [&]() { return VisitChildren(CS); });
}

bool VisitReturnStmt(const ReturnStmt *RS) {
Expand All @@ -279,17 +292,36 @@ class TrivialFunctionAnalysisVisitor

bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
bool VisitIfStmt(const IfStmt *IS) {
return WithCachedResult(IS, [&]() { return VisitChildren(IS); });
}
bool VisitForStmt(const ForStmt *FS) {
return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
}
bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
}
bool VisitWhileStmt(const WhileStmt *WS) {
return WithCachedResult(WS, [&]() { return VisitChildren(WS); });
}
bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }

bool VisitUnaryOperator(const UnaryOperator *UO) {
// Operator '*' and '!' are allowed as long as the operand is trivial.
if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
UO->getOpcode() == UO_LNot)
auto op = UO->getOpcode();
if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
return Visit(UO->getSubExpr());

if (UO->isIncrementOp() || UO->isDecrementOp()) {
// Allow increment or decrement of a POD type.
if (auto *RefExpr = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
if (auto *Decl = dyn_cast<VarDecl>(RefExpr->getDecl()))
return Decl->isLocalVarDeclOrParm() &&
Decl->getType().isPODType(Decl->getASTContext());
}
}
// Other operators are non-trivial.
return false;
}
Expand All @@ -304,22 +336,6 @@ class TrivialFunctionAnalysisVisitor
return VisitChildren(CO);
}

bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
if (auto *decl = DRE->getDecl()) {
if (isa<ParmVarDecl>(decl))
return true;
if (isa<EnumConstantDecl>(decl))
return true;
if (auto *VD = dyn_cast<VarDecl>(decl)) {
if (VD->hasConstantInitialization() && VD->getEvaluatedValue())
return true;
auto *Init = VD->getInit();
return !Init || Visit(Init);
}
}
return false;
}

bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); }

bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
Expand Down Expand Up @@ -436,6 +452,11 @@ class TrivialFunctionAnalysisVisitor
return true;
}

bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
// The use of a variable is trivial.
return true;
}

// Constant literal expressions are always trivial
bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
Expand All @@ -449,7 +470,7 @@ class TrivialFunctionAnalysisVisitor
}

private:
CacheTy Cache;
CacheTy &Cache;
};

bool TrivialFunctionAnalysis::isTrivialImpl(
Expand All @@ -474,4 +495,17 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
return Result;
}

bool TrivialFunctionAnalysis::isTrivialImpl(
const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
// If the statement isn't in the cache, conservatively assume that
// it's not trivial until analysis completes. Unlike a function case,
// we don't insert an entry into the cache until Visit returns
// since Visit* functions themselves make use of the cache.

TrivialFunctionAnalysisVisitor V(Cache);
bool Result = V.Visit(S);
assert(Cache.contains(S) && "Top-level statement not properly cached!");
return Result;
}

} // namespace clang
7 changes: 6 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
#include <optional>

namespace clang {
Expand All @@ -19,6 +20,7 @@ class CXXMethodDecl;
class CXXRecordDecl;
class Decl;
class FunctionDecl;
class Stmt;
class Type;

// Ref-countability of a type is implicitly defined by Ref<T> and RefPtr<T>
Expand Down Expand Up @@ -71,14 +73,17 @@ class TrivialFunctionAnalysis {
public:
/// \returns true if \p D is a "trivial" function.
bool isTrivial(const Decl *D) const { return isTrivialImpl(D, TheCache); }
bool isTrivial(const Stmt *S) const { return isTrivialImpl(S, TheCache); }

private:
friend class TrivialFunctionAnalysisVisitor;

using CacheTy = llvm::DenseMap<const Decl *, bool>;
using CacheTy =
llvm::DenseMap<llvm::PointerUnion<const Decl *, const Stmt *>, bool>;
mutable CacheTy TheCache{};

static bool isTrivialImpl(const Decl *D, CacheTy &Cache);
static bool isTrivialImpl(const Stmt *S, CacheTy &Cache);
};

} // namespace clang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,6 @@ using namespace ento;

namespace {

// for ( int a = ...) ... true
// for ( int a : ...) ... true
// if ( int* a = ) ... true
// anything else ... false
bool isDeclaredInForOrIf(const VarDecl *Var) {
assert(Var);
auto &ASTCtx = Var->getASTContext();
auto parent = ASTCtx.getParents(*Var);

if (parent.size() == 1) {
if (auto *DS = parent.begin()->get<DeclStmt>()) {
DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
if (grandParent.size() == 1) {
return grandParent.begin()->get<ForStmt>() ||
grandParent.begin()->get<IfStmt>() ||
grandParent.begin()->get<CXXForRangeStmt>();
}
}
}
return false;
}

// FIXME: should be defined by anotations in the future
bool isRefcountedStringsHack(const VarDecl *V) {
assert(V);
Expand Down Expand Up @@ -143,6 +121,11 @@ class UncountedLocalVarsChecker
// want to visit those, so we make our own RecursiveASTVisitor.
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
const UncountedLocalVarsChecker *Checker;

TrivialFunctionAnalysis TFA;

using Base = RecursiveASTVisitor<LocalVisitor>;

explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
: Checker(Checker) {
assert(Checker);
Expand All @@ -155,6 +138,36 @@ class UncountedLocalVarsChecker
Checker->visitVarDecl(V);
return true;
}

bool TraverseIfStmt(IfStmt *IS) {
if (!TFA.isTrivial(IS))
return Base::TraverseIfStmt(IS);
return true;
}

bool TraverseForStmt(ForStmt *FS) {
if (!TFA.isTrivial(FS))
return Base::TraverseForStmt(FS);
return true;
}

bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) {
if (!TFA.isTrivial(FRS))
return Base::TraverseCXXForRangeStmt(FRS);
return true;
}

bool TraverseWhileStmt(WhileStmt *WS) {
if (!TFA.isTrivial(WS))
return Base::TraverseWhileStmt(WS);
return true;
}

bool TraverseCompoundStmt(CompoundStmt *CS) {
if (!TFA.isTrivial(CS))
return Base::TraverseCompoundStmt(CS);
return true;
}
};

LocalVisitor visitor(this);
Expand Down Expand Up @@ -189,18 +202,16 @@ class UncountedLocalVarsChecker
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
const auto *MaybeGuardianArgType =
MaybeGuardian->getType().getTypePtr();
if (!MaybeGuardianArgType)
return;
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
MaybeGuardianArgType->getAsCXXRecordDecl();
if (!MaybeGuardianArgCXXRecord)
return;

if (MaybeGuardian->isLocalVarDecl() &&
(isRefCounted(MaybeGuardianArgCXXRecord) ||
isRefcountedStringsHack(MaybeGuardian)) &&
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
return;
if (MaybeGuardianArgType) {
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
MaybeGuardianArgType->getAsCXXRecordDecl();
if (MaybeGuardianArgCXXRecord) {
if (MaybeGuardian->isLocalVarDecl() &&
(isRefCounted(MaybeGuardianArgCXXRecord) ||
isRefcountedStringsHack(MaybeGuardian)) &&
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
return;
}
}

// Parameters are guaranteed to be safe for the duration of the call
Expand All @@ -219,9 +230,6 @@ class UncountedLocalVarsChecker
if (!V->isLocalVarDecl())
return true;

if (isDeclaredInForOrIf(V))
return true;

return false;
}

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Testing/CommandLineArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ std::vector<std::string> getCommandLineArgsForTesting(TestLanguage Lang) {
case Lang_CXX20:
Args = {"-std=c++20", "-frtti"};
break;
case Lang_CXX23:
Args = {"-std=c++23", "-frtti"};
break;
case Lang_OBJC:
Args = {"-x", "objective-c", "-frtti", "-fobjc-nonfragile-abi"};
break;
Expand Down Expand Up @@ -73,6 +76,9 @@ std::vector<std::string> getCC1ArgsForTesting(TestLanguage Lang) {
case Lang_CXX20:
Args = {"-std=c++20"};
break;
case Lang_CXX23:
Args = {"-std=c++23"};
break;
case Lang_OBJC:
Args = {"-xobjective-c"};
break;
Expand All @@ -96,6 +102,7 @@ StringRef getFilenameForTesting(TestLanguage Lang) {
case Lang_CXX14:
case Lang_CXX17:
case Lang_CXX20:
case Lang_CXX23:
return "input.cc";

case Lang_OpenCL:
Expand Down
8 changes: 5 additions & 3 deletions clang/test/AST/Interp/complex.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both -Wno-unused-value %s
// RUN: %clang_cc1 -verify=ref,both -Wno-unused-value %s

// expected-no-diagnostics
// ref-no-diagnostics

void blah() {
__complex__ unsigned xx;
__complex__ signed yy;
Expand All @@ -12,3 +9,8 @@ void blah() {
/// The following line calls into the constant interpreter.
result = xx * yy;
}


_Static_assert((0.0 + 0.0j) == (0.0 + 0.0j), "");
_Static_assert((0.0 + 0.0j) != (0.0 + 0.0j), ""); // both-error {{static assertion}} \
// both-note {{evaluates to}}
47 changes: 47 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,50 @@ namespace Builtin {

constexpr _Complex float C = __builtin_complex(10.0f, 20.0); // both-error {{arguments are of different types}}
}

namespace Cmp {
static_assert((0.0 + 0.0j) == (0.0 + 0.0j));
static_assert((0.0 + 0.0j) != (0.0 + 0.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}

static_assert((0.0 + 0.0j) == 0.0);
static_assert(0.0 == (0.0 + 0.0j));
static_assert(0.0 == 0.0j);
static_assert((0.0 + 1.0j) != 0.0);
static_assert(1.0 != (0.0 + 0.0j));
static_assert(0.0 != 1.0j);

// Walk around the complex plane stepping between angular differences and
// equality.
static_assert((1.0 + 0.0j) == (0.0 + 0.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((1.0 + 0.0j) == (1.0 + 0.0j));
static_assert((1.0 + 1.0j) == (1.0 + 0.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((1.0 + 1.0j) == (1.0 + 1.0j));
static_assert((0.0 + 1.0j) == (1.0 + 1.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((0.0 + 1.0j) == (0.0 + 1.0j));
static_assert((-1.0 + 1.0j) == (0.0 + 1.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((-1.0 + 1.0j) == (-1.0 + 1.0j));
static_assert((-1.0 + 0.0j) == (-1.0 + 1.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((-1.0 + 0.0j) == (-1.0 + 0.0j));
static_assert((-1.0 - 1.0j) == (-1.0 + 0.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((-1.0 - 1.0j) == (-1.0 - 1.0j));
static_assert((0.0 - 1.0j) == (-1.0 - 1.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((0.0 - 1.0j) == (0.0 - 1.0j));
static_assert((1.0 - 1.0j) == (0.0 - 1.0j)); // both-error {{static assertion}} \
// both-note {{evaluates to}}
static_assert((1.0 - 1.0j) == (1.0 - 1.0j));

/// Make sure these are rejected before reaching the constexpr interpreter.
static_assert((0.0 + 0.0j) & (0.0 + 0.0j)); // both-error {{invalid operands to binary expression}}
static_assert((0.0 + 0.0j) | (0.0 + 0.0j)); // both-error {{invalid operands to binary expression}}
static_assert((0.0 + 0.0j) < (0.0 + 0.0j)); // both-error {{invalid operands to binary expression}}
static_assert((0.0 + 0.0j) > (0.0 + 0.0j)); // both-error {{invalid operands to binary expression}}
static_assert((0.0 + 0.0j) ^ (0.0 + 0.0j)); // both-error {{invalid operands to binary expression}}
}
59 changes: 18 additions & 41 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
@@ -1,82 +1,58 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref20,all %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref20,all,all-20 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref23,all %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all-20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all %s -fexperimental-new-constant-interpreter

/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.

constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
// expected20-error {{constexpr function never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}

return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
// expected23-note {{initializer of 'm' is not a constant expression}}
return m; // expected20-note {{initializer of 'm' is not a constant expression}}
}
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
// expected20-error {{constexpr function never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}
return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
// expected23-note {{initializer of 'm' is not a constant expression}}
// expected20-note {{declared here}}
return m; // expected20-note {{initializer of 'm' is not a constant expression}}

}

constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
// expected20-error {{constexpr function never produces a constant expression}}
static _Thread_local int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}
return m; // expected20-note {{read of non-const variable}} \
// expected23-note {{read of non-const variable}}
// expected20-note {{declared here}}
return m; // expected20-note {{read of non-const variable}}
}


constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
// expected20-error {{constexpr function never produces a constant expression}}
static __thread int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}
return m; // expected20-note {{read of non-const variable}} \
// expected23-note {{read of non-const variable}}
// expected20-note {{declared here}}
return m; // expected20-note {{read of non-const variable}}
}

constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}}
return &m - &m;
}

constexpr int i(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int i(int n) { // ref20-error {{constexpr function never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return &m - &m;
}
Expand Down Expand Up @@ -132,8 +108,9 @@ namespace StaticOperators {
static_assert(f2() == 3);

struct S1 {
constexpr S1() { // all-error {{never produces a constant expression}}
throw; // all-note 2{{not valid in a constant expression}}
constexpr S1() { // all-20-error {{never produces a constant expression}}
throw; // all-note {{not valid in a constant expression}} \
// all-20-note {{not valid in a constant expression}}
}
static constexpr int operator()() { return 3; } // ref20-warning {{C++23 extension}} \
// expected20-warning {{C++23 extension}}
Expand Down
2 changes: 2 additions & 0 deletions clang/test/Analysis/Checkers/WebKit/mock-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ struct RefCountable {
static Ref<RefCountable> create();
void ref() {}
void deref() {}
void method();
int trivial() { return 123; }
};

template <typename T> T *downcast(T *t) { return t; }
Expand Down
101 changes: 95 additions & 6 deletions clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "mock-types.h"

void someFunction();

namespace raw_ptr {
void foo() {
RefCountable *bar;
Expand All @@ -16,6 +18,13 @@ void foo_ref() {
RefCountable automatic;
RefCountable &bar = automatic;
// expected-warning@-1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
someFunction();
bar.method();
}

void foo_ref_trivial() {
RefCountable automatic;
RefCountable &bar = automatic;
}

void bar_ref(RefCountable &) {}
Expand All @@ -32,6 +41,8 @@ void foo2() {
// missing embedded scope here
RefCountable *bar = foo.get();
// expected-warning@-1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
someFunction();
bar->method();
}

void foo3() {
Expand All @@ -47,11 +58,35 @@ void foo4() {
{ RefCountable *bar = foo.get(); }
}
}

void foo5() {
RefPtr<RefCountable> foo;
auto* bar = foo.get();
bar->trivial();
}

void foo6() {
RefPtr<RefCountable> foo;
auto* bar = foo.get();
// expected-warning@-1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
bar->method();
}

struct SelfReferencingStruct {
SelfReferencingStruct* ptr;
RefCountable* obj { nullptr };
};

void foo7(RefCountable* obj) {
SelfReferencingStruct bar = { &bar, obj };
bar.obj->method();
}

} // namespace guardian_scopes

namespace auto_keyword {
class Foo {
RefCountable *provide_ref_ctnbl() { return nullptr; }
RefCountable *provide_ref_ctnbl();

void evil_func() {
RefCountable *bar = provide_ref_ctnbl();
Expand All @@ -62,28 +97,44 @@ class Foo {
// expected-warning@-1{{Local variable 'baz2' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
[[clang::suppress]] auto *baz_suppressed = provide_ref_ctnbl(); // no-warning
}

void func() {
RefCountable *bar = provide_ref_ctnbl();
// expected-warning@-1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
if (bar)
bar->method();
}
};
} // namespace auto_keyword

namespace guardian_casts {
void foo1() {
RefPtr<RefCountable> foo;
{ RefCountable *bar = downcast<RefCountable>(foo.get()); }
{
RefCountable *bar = downcast<RefCountable>(foo.get());
bar->method();
}
foo->method();
}

void foo2() {
RefPtr<RefCountable> foo;
{
RefCountable *bar =
static_cast<RefCountable *>(downcast<RefCountable>(foo.get()));
someFunction();
}
}
} // namespace guardian_casts

namespace guardian_ref_conversion_operator {
void foo() {
Ref<RefCountable> rc;
{ RefCountable &rr = rc; }
{
RefCountable &rr = rc;
rr.method();
someFunction();
}
}
} // namespace guardian_ref_conversion_operator

Expand All @@ -92,9 +143,47 @@ RefCountable *provide_ref_ctnbl() { return nullptr; }

void foo() {
// no warnings
if (RefCountable *a = provide_ref_ctnbl()) { }
for (RefCountable *a = provide_ref_ctnbl(); a != nullptr;) { }
if (RefCountable *a = provide_ref_ctnbl())
a->trivial();
for (RefCountable *b = provide_ref_ctnbl(); b != nullptr;)
b->trivial();
RefCountable *array[1];
for (RefCountable *a : array) { }
for (RefCountable *c : array)
c->trivial();
while (RefCountable *d = provide_ref_ctnbl())
d->trivial();
do {
RefCountable *e = provide_ref_ctnbl();
e->trivial();
} while (1);
someFunction();
}

void bar() {
if (RefCountable *a = provide_ref_ctnbl()) {
// expected-warning@-1{{Local variable 'a' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
a->method();
}
for (RefCountable *b = provide_ref_ctnbl(); b != nullptr;) {
// expected-warning@-1{{Local variable 'b' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
b->method();
}
RefCountable *array[1];
for (RefCountable *c : array) {
// expected-warning@-1{{Local variable 'c' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
c->method();
}

while (RefCountable *d = provide_ref_ctnbl()) {
// expected-warning@-1{{Local variable 'd' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
d->method();
}
do {
RefCountable *e = provide_ref_ctnbl();
// expected-warning@-1{{Local variable 'e' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
e->method();
} while (1);
someFunction();
}

} // namespace ignore_for_if
27 changes: 23 additions & 4 deletions clang/test/Analysis/stream-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void clang_analyzer_dump(int);
void clang_analyzer_warnIfReached(void);
void StreamTesterChecker_make_feof_stream(FILE *);
void StreamTesterChecker_make_ferror_stream(FILE *);
void StreamTesterChecker_make_ferror_indeterminate_stream(FILE *);

void error_fopen(void) {
FILE *F = fopen("file", "r");
Expand Down Expand Up @@ -52,6 +53,8 @@ void stream_error_feof(void) {
clearerr(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
StreamTesterChecker_make_ferror_indeterminate_stream(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
fclose(F);
}

Expand All @@ -65,6 +68,8 @@ void stream_error_ferror(void) {
clearerr(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
StreamTesterChecker_make_ferror_indeterminate_stream(F);
clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
fclose(F);
}

Expand Down Expand Up @@ -233,7 +238,7 @@ void error_fscanf(int *A) {
fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}}
}

void error_ungetc() {
void error_ungetc(int TestIndeterminate) {
FILE *F = tmpfile();
if (!F)
return;
Expand All @@ -245,8 +250,12 @@ void error_ungetc() {
clang_analyzer_eval(Ret == 'X'); // expected-warning {{TRUE}}
}
fputc('Y', F); // no-warning
if (TestIndeterminate) {
StreamTesterChecker_make_ferror_indeterminate_stream(F);
ungetc('X', F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
ungetc('A', F); // expected-warning {{Stream might be already closed}}
ungetc('A', F); // expected-warning {{Stream might be already closed}}
}

void error_getdelim(char *P, size_t Sz) {
Expand Down Expand Up @@ -449,7 +458,7 @@ void error_fseeko_0(void) {
fclose(F);
}

void error_ftell(void) {
void error_ftell(int TestIndeterminate) {
FILE *F = fopen("file", "r");
if (!F)
return;
Expand All @@ -467,10 +476,14 @@ void error_ftell(void) {
rc = ftell(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
if (TestIndeterminate) {
StreamTesterChecker_make_ferror_indeterminate_stream(F);
ftell(F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
}

void error_ftello(void) {
void error_ftello(int TestIndeterminate) {
FILE *F = fopen("file", "r");
if (!F)
return;
Expand All @@ -488,6 +501,10 @@ void error_ftello(void) {
rc = ftello(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
if (TestIndeterminate) {
StreamTesterChecker_make_ferror_indeterminate_stream(F);
ftell(F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
}

Expand All @@ -506,6 +523,8 @@ void error_fileno(void) {
N = fileno(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
StreamTesterChecker_make_ferror_indeterminate_stream(F);
fileno(F); // no warning
fclose(F);
}

Expand Down
2 changes: 2 additions & 0 deletions clang/test/CXX/basic/basic.link/p10-ex2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
//
// RUN: %clang_cc1 -std=c++20 M.cpp -fsyntax-only -DTEST_INTERFACE -verify
// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm
// RUN: %clang_cc1 -std=c++20 M.cpp -emit-reduced-module-interface -o M.reduced.pcm
// RUN: %clang_cc1 -std=c++20 useM.cpp -fsyntax-only -fmodule-file=M=M.pcm -verify
// RUN: %clang_cc1 -std=c++20 useM.cpp -fsyntax-only -fmodule-file=M=M.reduced.pcm -verify

//--- decls.h
int f(); // #1, attached to the global module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Friend-in-reachable-class.cppm -o %t/X.pcm
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
// RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/Friend-in-reachable-class.cppm \
// RUN: -o %t/X.reduced.pcm
// RUN: %clang_cc1 -std=c++20 -fmodule-file=X=%t/X.pcm %t/Use.cpp -verify -fsyntax-only
// RUN: %clang_cc1 -std=c++20 -fmodule-file=X=%t/X.reduced.pcm %t/Use.cpp -verify -fsyntax-only
//
//--- Friend-in-reachable-class.cppm
module;
Expand Down
40 changes: 21 additions & 19 deletions clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// This test is for the [class.compare.default]p3 added by P2002R0
// Also covers modifications made by P2448R2 and extension warnings
// Also covers modifications made by P2448R2

// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -Wc++23-default-comp-relaxed-constexpr -verify=expected,extension %s
// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a %s
// RUN: %clang_cc1 -std=c++23 -verify=expected %s

namespace std {
struct strong_ordering {
Expand Down Expand Up @@ -82,10 +82,12 @@ struct TestB {
};

struct C {
friend bool operator==(const C&, const C&); // expected-note {{previous}} extension-note 2{{non-constexpr comparison function declared here}}
friend bool operator==(const C&, const C&); // expected-note {{previous}} \
// cxx2a-note 2{{declared here}}
friend bool operator!=(const C&, const C&) = default; // expected-note {{previous}}

friend std::strong_ordering operator<=>(const C&, const C&); // expected-note {{previous}} extension-note 2{{non-constexpr comparison function declared here}}
friend std::strong_ordering operator<=>(const C&, const C&); // expected-note {{previous}} \
// cxx2a-note 2{{declared here}}
friend bool operator<(const C&, const C&) = default; // expected-note {{previous}}
friend bool operator<=(const C&, const C&) = default; // expected-note {{previous}}
friend bool operator>(const C&, const C&) = default; // expected-note {{previous}}
Expand Down Expand Up @@ -129,38 +131,38 @@ struct TestD {

struct E {
A a;
C c; // extension-note 2{{non-constexpr comparison function would be used to compare member 'c'}}
C c; // cxx2a-note 2{{non-constexpr comparison function would be used to compare member 'c'}}
A b;
friend constexpr bool operator==(const E&, const E&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr bool operator==(const E&, const E&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator!=(const E&, const E&) = default;

friend constexpr std::strong_ordering operator<=>(const E&, const E&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr std::strong_ordering operator<=>(const E&, const E&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator<(const E&, const E&) = default;
friend constexpr bool operator<=(const E&, const E&) = default;
friend constexpr bool operator>(const E&, const E&) = default;
friend constexpr bool operator>=(const E&, const E&) = default;
};

struct E2 : A, C { // extension-note 2{{non-constexpr comparison function would be used to compare base class 'C'}}
friend constexpr bool operator==(const E2&, const E2&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
struct E2 : A, C { // cxx2a-note 2{{non-constexpr comparison function would be used to compare base class 'C'}}
friend constexpr bool operator==(const E2&, const E2&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator!=(const E2&, const E2&) = default;

friend constexpr std::strong_ordering operator<=>(const E2&, const E2&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr std::strong_ordering operator<=>(const E2&, const E2&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator<(const E2&, const E2&) = default;
friend constexpr bool operator<=(const E2&, const E2&) = default;
friend constexpr bool operator>(const E2&, const E2&) = default;
friend constexpr bool operator>=(const E2&, const E2&) = default;
};

struct F {
friend bool operator==(const F&, const F&); // extension-note {{non-constexpr comparison function declared here}}
friend constexpr bool operator!=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}

friend std::strong_ordering operator<=>(const F&, const F&); // extension-note 4{{non-constexpr comparison function declared here}}
friend constexpr bool operator<(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr bool operator<=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr bool operator>(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend constexpr bool operator>=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
friend bool operator==(const F&, const F&); // cxx2a-note {{declared here}}
friend constexpr bool operator!=(const F&, const F&) = default; // cxx2a-error {{cannot be declared constexpr}}

friend std::strong_ordering operator<=>(const F&, const F&); // cxx2a-note 4{{non-constexpr comparison function declared here}}
friend constexpr bool operator<(const F&, const F&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator<=(const F&, const F&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator>(const F&, const F&) = default; // cxx2a-error {{cannot be declared constexpr}}
friend constexpr bool operator>=(const F&, const F&) = default; // cxx2a-error {{cannot be declared constexpr}}
};

// No implicit 'constexpr' if it's not the first declaration.
Expand Down
20 changes: 10 additions & 10 deletions clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -Wc++23-default-comp-relaxed-constexpr -verify=expected,extension %s
// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a %s
// RUN: %clang_cc1 -std=c++23 -verify=expected %s

// This test is for [class.compare.default]p3 as modified and renumbered to p4
// by P2002R0.
// Also covers modifications made by P2448R2 and extension warnings
// Also covers modifications made by P2448R2

namespace std {
struct strong_ordering {
Expand Down Expand Up @@ -78,13 +78,13 @@ void use_g(G g) {
}

struct H {
bool operator==(const H&) const; // extension-note {{non-constexpr comparison function declared here}}
bool operator==(const H&) const; // cxx2a-note {{non-constexpr comparison function declared here}}
constexpr std::strong_ordering operator<=>(const H&) const { return std::strong_ordering::equal; }
};

struct I {
H h; // extension-note {{non-constexpr comparison function would be used to compare member 'h'}}
constexpr std::strong_ordering operator<=>(const I&) const = default; // extension-warning {{implicit 'operator==' invokes a non-constexpr comparison function is a C++23 extension}}
H h; // cxx2a-note {{non-constexpr comparison function would be used to compare member 'h'}}
constexpr std::strong_ordering operator<=>(const I&) const = default; // cxx2a-error {{cannot be declared constexpr}}
};

struct J {
Expand Down Expand Up @@ -148,16 +148,16 @@ namespace NoInjectionIfOperatorEqualsDeclared {

namespace GH61238 {
template <typename A> struct my_struct {
A value; // extension-note {{non-constexpr comparison function would be used to compare member 'value'}}
A value; // cxx2a-note {{non-constexpr comparison function would be used to compare member 'value'}}

constexpr friend bool operator==(const my_struct &, const my_struct &) noexcept = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++23 extension}}
constexpr friend bool operator==(const my_struct &, const my_struct &) noexcept = default; // cxx2a-error {{cannot be declared constexpr}}
};

struct non_constexpr_type {
friend bool operator==(non_constexpr_type, non_constexpr_type) noexcept { // extension-note {{non-constexpr comparison function declared here}}
friend bool operator==(non_constexpr_type, non_constexpr_type) noexcept { // cxx2a-note {{non-constexpr comparison function declared here}}
return false;
}
};

my_struct<non_constexpr_type> obj; // extension-note {{in instantiation of template class 'GH61238::my_struct<GH61238::non_constexpr_type>' requested here}}
my_struct<non_constexpr_type> obj; // cxx2a-note {{in instantiation of template class 'GH61238::my_struct<GH61238::non_constexpr_type>' requested here}}
}
8 changes: 4 additions & 4 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ namespace subobject {
struct A {
~A();
};
struct B : A { // expected-note {{here}}
constexpr ~B() {} // expected-error {{destructor cannot be declared constexpr because base class 'A' does not have a constexpr destructor}}
struct B : A { // cxx2a-note {{here}}
constexpr ~B() {} // cxx2a-error {{destructor cannot be declared constexpr because base class 'A' does not have a constexpr destructor}}
};
struct C {
A a; // expected-note {{here}}
constexpr ~C() {} // expected-error {{destructor cannot be declared constexpr because data member 'a' does not have a constexpr destructor}}
A a; // cxx2a-note {{here}}
constexpr ~C() {} // cxx2a-error {{destructor cannot be declared constexpr because data member 'a' does not have a constexpr destructor}}
};
struct D : A {
A a;
Expand Down
10 changes: 4 additions & 6 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ constexpr int i(int n) {
return m;
}

constexpr int g() { // expected-error {{constexpr function never produces a constant expression}}
goto test; // expected-note {{subexpression not valid in a constant expression}} \
// expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++23}}
constexpr int g() {
goto test; // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++23}}
test:
return 0;
}
Expand All @@ -29,9 +28,8 @@ struct NonLiteral { // expected-note 2 {{'NonLiteral' is not literal}}
NonLiteral() {}
};

constexpr void non_literal() { // expected-error {{constexpr function never produces a constant expression}}
NonLiteral n; // expected-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} \
// expected-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}}
constexpr void non_literal() {
NonLiteral n; // expected-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}}
}

constexpr void non_literal2(bool b) {
Expand Down
18 changes: 9 additions & 9 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,beforecxx14,beforecxx20,beforecxx23 -std=c++11 %s
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,aftercxx14,beforecxx20,beforecxx23 -std=c++14 %s
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,aftercxx14,aftercxx20,beforecxx23 -std=c++20 %s
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,aftercxx14,beforecxx20,beforecxx23,cxx14_20 -std=c++14 %s
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,aftercxx14,aftercxx20,beforecxx23,cxx14_20 -std=c++20 %s
// RUN: %clang_cc1 -fcxx-exceptions -verify=expected,aftercxx14,aftercxx20 -std=c++23 %s

namespace N {
Expand All @@ -11,7 +11,7 @@ namespace M {
typedef double D;
}

struct NonLiteral { // expected-note 2{{no constexpr constructors}}
struct NonLiteral { // beforecxx23-note 2{{no constexpr constructors}}
NonLiteral() {}
NonLiteral(int) {}
};
Expand Down Expand Up @@ -43,7 +43,7 @@ struct T : SS, NonLiteral {
// - its return type shall be a literal type;
// Once we support P2448R2 constexpr functions will be allowd to return non-literal types
// The destructor will also be allowed
constexpr NonLiteral NonLiteralReturn() const { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}}
constexpr NonLiteral NonLiteralReturn() const { return {}; } // beforecxx23-error {{constexpr function's return type 'NonLiteral' is not a literal type}}
constexpr void VoidReturn() const { return; } // beforecxx14-error {{constexpr function's return type 'void' is not a literal type}}
constexpr ~T(); // beforecxx20-error {{destructor cannot be declared constexpr}}

Expand All @@ -52,7 +52,7 @@ struct T : SS, NonLiteral {

// - each of its parameter types shall be a literal type;
// Once we support P2448R2 constexpr functions will be allowd to have parameters of non-literal types
constexpr int NonLiteralParam(NonLiteral) const { return 0; } // expected-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}}
constexpr int NonLiteralParam(NonLiteral) const { return 0; } // beforecxx23-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}}
typedef int G(NonLiteral) const;
constexpr G NonLiteralParam2; // ok until definition

Expand All @@ -66,7 +66,7 @@ struct T : SS, NonLiteral {
// constexpr since they can't be const.
constexpr T &operator=(const T &) = default; // beforecxx14-error {{an explicitly-defaulted copy assignment operator may not have 'const', 'constexpr' or 'volatile' qualifiers}} \
// beforecxx14-warning {{C++14}} \
// aftercxx14-error{{defaulted definition of copy assignment operator is not constexpr}}
// cxx14_20-error{{defaulted definition of copy assignment operator cannot be marked constexpr}}
};

constexpr int T::OutOfLineVirtual() const { return 0; }
Expand Down Expand Up @@ -229,9 +229,9 @@ namespace DR1364 {
return k; // ok, even though lvalue-to-rvalue conversion of a function
// parameter is not allowed in a constant expression.
}
int kGlobal; // expected-note {{here}}
constexpr int f() { // expected-error {{constexpr function never produces a constant expression}}
return kGlobal; // expected-note {{read of non-const}}
int kGlobal; // beforecxx23-note {{here}}
constexpr int f() { // beforecxx23-error {{constexpr function never produces a constant expression}}
return kGlobal; // beforecxx23-note {{read of non-const}}
}
}

Expand Down
8 changes: 4 additions & 4 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ struct X {

union XU1 { int a; constexpr XU1() = default; };
#ifndef CXX2A
// expected-error@-2{{not constexpr}}
// expected-error@-2{{cannot be marked constexpr}}
#endif
union XU2 { int a = 1; constexpr XU2() = default; };

Expand All @@ -282,7 +282,7 @@ struct XU3 {
};
constexpr XU3() = default;
#ifndef CXX2A
// expected-error@-2{{not constexpr}}
// expected-error@-2{{cannot be marked constexpr}}
#endif
};
struct XU4 {
Expand Down Expand Up @@ -333,7 +333,7 @@ namespace CtorLookup {
constexpr B(B&);
};
constexpr B::B(const B&) = default;
constexpr B::B(B&) = default; // expected-error {{not constexpr}}
constexpr B::B(B&) = default; // expected-error {{cannot be marked constexpr}}

struct C {
A a;
Expand All @@ -342,7 +342,7 @@ namespace CtorLookup {
constexpr C(C&);
};
constexpr C::C(const C&) = default;
constexpr C::C(C&) = default; // expected-error {{not constexpr}}
constexpr C::C(C&) = default; // expected-error {{cannot be marked constexpr}}
}

namespace PR14503 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// An explicitly-defaulted function may be declared constexpr only if it would
// have been implicitly declared as constexpr.
struct S1 {
constexpr S1() = default; // expected-error {{defaulted definition of default constructor is not constexpr}}
constexpr S1() = default; // expected-error {{defaulted definition of default constructor cannot be marked constexpr}}
constexpr S1(const S1&) = default;
constexpr S1(S1&&) = default;
constexpr S1 &operator=(const S1&) const = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}}
Expand All @@ -18,8 +18,8 @@ struct NoCopyMove {
};
struct S2 {
constexpr S2() = default;
constexpr S2(const S2&) = default; // expected-error {{defaulted definition of copy constructor is not constexpr}}
constexpr S2(S2&&) = default; // expected-error {{defaulted definition of move constructor is not constexpr}}
constexpr S2(const S2&) = default; // expected-error {{defaulted definition of copy constructor cannot be marked constexpr}}
constexpr S2(S2&&) = default; // expected-error {{defaulted definition of move constructor cannot be marked}}
NoCopyMove ncm;
};

Expand Down
Loading