23,074 changes: 11,662 additions & 11,412 deletions clang/include/clang/Sema/Sema.h

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/PCHContainerOperations.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- Serialization/PCHContainerOperations.h - PCH Containers --*- C++ -*-===//
//===-- PCHContainerOperations.h - PCH Containers ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- PathDiagnosticConsumers.h - Path Diagnostic Clients ------*- C++ -*-===//
//===--- PathDiagnosticConsumers.h - Path Diagnostic Clients ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERHELPERS_H
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERHELPERS_H

#include "ProgramState_Fwd.h"
#include "SVals.h"

#include "clang/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/OperatorKinds.h"
Expand Down Expand Up @@ -110,6 +113,9 @@ class OperatorKind {
OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,
bool IsBinary);

std::optional<DefinedSVal> getPointeeDefVal(SVal PtrSVal,
ProgramStateRef State);

} // namespace ento

} // namespace clang
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/ARCMigrate/TransGCAttrs.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===//
//===--- TransGCAttrs.cpp - Transformations to ARC mode -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
25 changes: 16 additions & 9 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2465,7 +2465,7 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {

// OpenCL permits const integral variables to be used in constant
// expressions, like in C++98.
if (!Lang.CPlusPlus && !Lang.OpenCL)
if (!Lang.CPlusPlus && !Lang.OpenCL && !Lang.C23)
return false;

// Function parameters are never usable in constant expressions.
Expand All @@ -2487,14 +2487,19 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
if (!getType().isConstant(C) || getType().isVolatileQualified())
return false;

// In C++, const, non-volatile variables of integral or enumeration types
// can be used in constant expressions.
if (getType()->isIntegralOrEnumerationType())
// In C++, but not in C, const, non-volatile variables of integral or
// enumeration types can be used in constant expressions.
if (getType()->isIntegralOrEnumerationType() && !Lang.C23)
return true;

// C23 6.6p7: An identifier that is:
// ...
// - declared with storage-class specifier constexpr and has an object type,
// is a named constant, ... such a named constant is a constant expression
// with the type and value of the declared object.
// Additionally, in C++11, non-volatile constexpr variables can be used in
// constant expressions.
return Lang.CPlusPlus11 && isConstexpr();
return (Lang.CPlusPlus11 || Lang.C23) && isConstexpr();
}

bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const {
Expand Down Expand Up @@ -2572,11 +2577,11 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
IsConstantInitialization);

// In C++, this isn't a constant initializer if we produced notes. In that
// In C++/C23, this isn't a constant initializer if we produced notes. In that
// case, we can't keep the result, because it may only be correct under the
// assumption that the initializer is a constant context.
if (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus &&
!Notes.empty())
if (IsConstantInitialization &&
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) && !Notes.empty())
Result = false;

// Ensure the computed APValue is cleaned up later if evaluation succeeded,
Expand Down Expand Up @@ -2634,7 +2639,9 @@ bool VarDecl::checkForConstantInitialization(
// std::is_constant_evaluated()).
assert(!Eval->WasEvaluated &&
"already evaluated var value before checking for constant init");
assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
assert((getASTContext().getLangOpts().CPlusPlus ||
getASTContext().getLangOpts().C23) &&
"only meaningful in C++/C23");

assert(!getInit()->isValueDependent());

Expand Down
10 changes: 9 additions & 1 deletion clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4133,6 +4133,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}

bool IsConstant = BaseType.isConstant(Info.Ctx);
bool ConstexprVar = false;
if (const auto *VD = dyn_cast_if_present<VarDecl>(
Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()))
ConstexprVar = VD->isConstexpr();

// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const.
Expand All @@ -4152,6 +4156,9 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
return CompleteObject();
} else if (VD->isConstexpr()) {
// OK, we can read this variable.
} else if (Info.getLangOpts().C23 && ConstexprVar) {
Info.FFDiag(E);
return CompleteObject();
} else if (BaseType->isIntegralOrEnumerationType()) {
if (!IsConstant) {
if (!IsAccess)
Expand Down Expand Up @@ -15826,7 +15833,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
EStatus.Diag = &Notes;

EvalInfo Info(Ctx, EStatus,
(IsConstantInitialization && Ctx.getLangOpts().CPlusPlus)
(IsConstantInitialization &&
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
? EvalInfo::EM_ConstantExpression
: EvalInfo::EM_ConstantFold);
Info.setEvaluatingDecl(VD, Value);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeEmitter.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- ByteCodeEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===//
//===--- ByteCodeEmitter.h - Instruction emitter for the VM -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/FunctionPointer.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- FunctionPointer.h - Types for the constexpr VM ----------*- C++ -*-===//
//===--- FunctionPointer.h - Types for the constexpr VM ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,7 @@ enum class IncDecOp {

template <typename T, IncDecOp Op, PushVal DoPush>
bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (Ptr.isDummy())
return false;
assert(!Ptr.isDummy());

if constexpr (std::is_same_v<T, Boolean>) {
if (!S.getLangOpts().CPlusPlus14)
Expand Down Expand Up @@ -585,7 +584,7 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Inc(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
return false;
Expand All @@ -599,7 +598,7 @@ bool Inc(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool IncPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
return false;
Expand All @@ -614,7 +613,7 @@ bool IncPop(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Dec(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
return false;
Expand All @@ -628,7 +627,7 @@ bool Dec(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool DecPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
return false;
Expand Down Expand Up @@ -1224,7 +1223,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (S.inConstantContext() && !CheckNull(S, OpPC, Ptr, CSK_Field))
if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
!CheckNull(S, OpPC, Ptr, CSK_Field))
return false;

if (CheckDummy(S, OpPC, Ptr)) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/PrimType.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- PrimType.h - Types for the constexpr VM --------------------*- C++ -*-===//
//===--- PrimType.h - Types for the constexpr VM ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
8 changes: 0 additions & 8 deletions clang/lib/Basic/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,14 +722,6 @@ void VisibleModuleSet::setVisible(Module *M, SourceLocation Loc,
VisitModule({M, nullptr});
}

void VisibleModuleSet::makeTransitiveImportsVisible(Module *M,
SourceLocation Loc,
VisibleCallback Vis,
ConflictCallback Cb) {
for (auto *I : M->Imports)
setVisible(I, Loc, Vis, Cb);
}

ASTSourceDescriptor::ASTSourceDescriptor(Module &M)
: Signature(M.Signature), ClangModule(&M) {
if (M.Directory)
Expand Down
129 changes: 33 additions & 96 deletions clang/lib/Basic/OpenMPKinds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,31 +574,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
}

bool clang::isOpenMPLoopDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_simd || DKind == OMPD_for || DKind == OMPD_for_simd ||
DKind == OMPD_parallel_for || DKind == OMPD_parallel_for_simd ||
DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd ||
DKind == OMPD_master_taskloop || DKind == OMPD_master_taskloop_simd ||
DKind == OMPD_parallel_master_taskloop ||
DKind == OMPD_parallel_master_taskloop_simd ||
DKind == OMPD_masked_taskloop || DKind == OMPD_masked_taskloop_simd ||
DKind == OMPD_parallel_masked_taskloop || DKind == OMPD_distribute ||
DKind == OMPD_parallel_masked_taskloop_simd ||
DKind == OMPD_target_parallel_for ||
DKind == OMPD_distribute_parallel_for ||
DKind == OMPD_distribute_parallel_for_simd ||
DKind == OMPD_distribute_simd ||
DKind == OMPD_target_parallel_for_simd || DKind == OMPD_target_simd ||
DKind == OMPD_teams_distribute ||
DKind == OMPD_teams_distribute_simd ||
DKind == OMPD_teams_distribute_parallel_for_simd ||
DKind == OMPD_teams_distribute_parallel_for ||
DKind == OMPD_target_teams_distribute ||
DKind == OMPD_target_teams_distribute_parallel_for ||
DKind == OMPD_target_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_simd || DKind == OMPD_tile ||
DKind == OMPD_unroll || DKind == OMPD_loop ||
DKind == OMPD_teams_loop || DKind == OMPD_target_teams_loop ||
DKind == OMPD_parallel_loop || DKind == OMPD_target_parallel_loop;
return getDirectiveAssociation(DKind) == Association::Loop;
}

bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) {
Expand All @@ -619,44 +595,20 @@ bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) {
}

bool clang::isOpenMPTaskLoopDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd ||
DKind == OMPD_master_taskloop || DKind == OMPD_master_taskloop_simd ||
DKind == OMPD_parallel_master_taskloop ||
DKind == OMPD_masked_taskloop || DKind == OMPD_masked_taskloop_simd ||
DKind == OMPD_parallel_masked_taskloop ||
DKind == OMPD_parallel_masked_taskloop_simd ||
DKind == OMPD_parallel_master_taskloop_simd;
return DKind == OMPD_taskloop ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_taskloop);
}

bool clang::isOpenMPParallelDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_parallel || DKind == OMPD_parallel_for ||
DKind == OMPD_parallel_for_simd || DKind == OMPD_parallel_sections ||
DKind == OMPD_target_parallel || DKind == OMPD_target_parallel_for ||
DKind == OMPD_distribute_parallel_for ||
DKind == OMPD_distribute_parallel_for_simd ||
DKind == OMPD_target_parallel_for_simd ||
DKind == OMPD_teams_distribute_parallel_for ||
DKind == OMPD_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_parallel_for ||
DKind == OMPD_target_teams_distribute_parallel_for_simd ||
DKind == OMPD_parallel_master || DKind == OMPD_parallel_masked ||
DKind == OMPD_parallel_master_taskloop ||
DKind == OMPD_parallel_master_taskloop_simd ||
DKind == OMPD_parallel_masked_taskloop ||
DKind == OMPD_parallel_masked_taskloop_simd ||
DKind == OMPD_parallel_loop || DKind == OMPD_target_parallel_loop ||
DKind == OMPD_teams_loop;
if (DKind == OMPD_teams_loop)
return true;
return DKind == OMPD_parallel ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_parallel);
}

bool clang::isOpenMPTargetExecutionDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_target || DKind == OMPD_target_parallel ||
DKind == OMPD_target_parallel_for ||
DKind == OMPD_target_parallel_for_simd || DKind == OMPD_target_simd ||
DKind == OMPD_target_teams || DKind == OMPD_target_teams_distribute ||
DKind == OMPD_target_teams_distribute_parallel_for ||
DKind == OMPD_target_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_simd ||
DKind == OMPD_target_teams_loop || DKind == OMPD_target_parallel_loop;
return DKind == OMPD_target ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_target);
}

bool clang::isOpenMPTargetDataManagementDirective(OpenMPDirectiveKind DKind) {
Expand All @@ -665,60 +617,45 @@ bool clang::isOpenMPTargetDataManagementDirective(OpenMPDirectiveKind DKind) {
}

bool clang::isOpenMPNestingTeamsDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_teams || DKind == OMPD_teams_distribute ||
DKind == OMPD_teams_distribute_simd ||
DKind == OMPD_teams_distribute_parallel_for_simd ||
DKind == OMPD_teams_distribute_parallel_for ||
DKind == OMPD_teams_loop;
if (DKind == OMPD_teams)
return true;
ArrayRef<Directive> Leaves = getLeafConstructs(DKind);
return !Leaves.empty() && Leaves.front() == OMPD_teams;
}

bool clang::isOpenMPTeamsDirective(OpenMPDirectiveKind DKind) {
return isOpenMPNestingTeamsDirective(DKind) || DKind == OMPD_target_teams ||
DKind == OMPD_target_teams_distribute ||
DKind == OMPD_target_teams_distribute_parallel_for ||
DKind == OMPD_target_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_simd ||
DKind == OMPD_target_teams_loop;
return DKind == OMPD_teams ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_teams);
}

bool clang::isOpenMPSimdDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_simd || DKind == OMPD_for_simd ||
DKind == OMPD_parallel_for_simd || DKind == OMPD_taskloop_simd ||
DKind == OMPD_master_taskloop_simd ||
DKind == OMPD_masked_taskloop_simd ||
DKind == OMPD_parallel_master_taskloop_simd ||
DKind == OMPD_parallel_masked_taskloop_simd ||
DKind == OMPD_distribute_parallel_for_simd ||
DKind == OMPD_distribute_simd || DKind == OMPD_target_simd ||
DKind == OMPD_teams_distribute_simd ||
DKind == OMPD_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_parallel_for_simd ||
DKind == OMPD_target_teams_distribute_simd ||
DKind == OMPD_target_parallel_for_simd;
// Avoid OMPD_declare_simd
if (getDirectiveAssociation(DKind) != Association::Loop)
return false;
// Formally, OMPD_end_do_simd also has a loop association, but
// it's a Fortran-specific directive.

return DKind == OMPD_simd ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_simd);
}

bool clang::isOpenMPNestingDistributeDirective(OpenMPDirectiveKind Kind) {
return Kind == OMPD_distribute || Kind == OMPD_distribute_parallel_for ||
Kind == OMPD_distribute_parallel_for_simd ||
Kind == OMPD_distribute_simd;
// TODO add next directives.
if (Kind == OMPD_distribute)
return true;
ArrayRef<Directive> Leaves = getLeafConstructs(Kind);
return !Leaves.empty() && Leaves.front() == OMPD_distribute;
}

bool clang::isOpenMPDistributeDirective(OpenMPDirectiveKind Kind) {
return isOpenMPNestingDistributeDirective(Kind) ||
Kind == OMPD_teams_distribute || Kind == OMPD_teams_distribute_simd ||
Kind == OMPD_teams_distribute_parallel_for_simd ||
Kind == OMPD_teams_distribute_parallel_for ||
Kind == OMPD_target_teams_distribute ||
Kind == OMPD_target_teams_distribute_parallel_for ||
Kind == OMPD_target_teams_distribute_parallel_for_simd ||
Kind == OMPD_target_teams_distribute_simd;
return Kind == OMPD_distribute ||
llvm::is_contained(getLeafConstructs(Kind), OMPD_distribute);
}

bool clang::isOpenMPGenericLoopDirective(OpenMPDirectiveKind Kind) {
return Kind == OMPD_loop || Kind == OMPD_teams_loop ||
Kind == OMPD_target_teams_loop || Kind == OMPD_parallel_loop ||
Kind == OMPD_target_parallel_loop;
if (Kind == OMPD_loop)
return true;
ArrayRef<Directive> Leaves = getLeafConstructs(Kind);
return !Leaves.empty() && Leaves.back() == OMPD_loop;
}

bool clang::isOpenMPPrivate(OpenMPClauseKind Kind) {
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18439,6 +18439,17 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
CGM.getIntrinsic(Intrinsic::amdgcn_global_load_tr, {ArgTy});
return Builder.CreateCall(F, {Addr});
}
case AMDGPU::BI__builtin_amdgcn_get_fpenv: {
Function *F = CGM.getIntrinsic(Intrinsic::get_fpenv,
{llvm::Type::getInt64Ty(getLLVMContext())});
return Builder.CreateCall(F);
}
case AMDGPU::BI__builtin_amdgcn_set_fpenv: {
Function *F = CGM.getIntrinsic(Intrinsic::set_fpenv,
{llvm::Type::getInt64Ty(getLLVMContext())});
llvm::Value *Env = EmitScalarExpr(E->getArg(0));
return Builder.CreateCall(F, {Env});
}
case AMDGPU::BI__builtin_amdgcn_read_exec:
return EmitAMDGCNBallotForExec(*this, E, Int64Ty, Int64Ty, false);
case AMDGPU::BI__builtin_amdgcn_read_exec_lo:
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -872,12 +872,12 @@ void CodeGenModule::Release() {
EmitMainVoidAlias();

if (getTriple().isAMDGPU()) {
// Emit amdgpu_code_object_version module flag, which is code object version
// Emit amdhsa_code_object_version module flag, which is code object version
// times 100.
if (getTarget().getTargetOpts().CodeObjectVersion !=
llvm::CodeObjectVersionKind::COV_None) {
getModule().addModuleFlag(llvm::Module::Error,
"amdgpu_code_object_version",
"amdhsa_code_object_version",
getTarget().getTargetOpts().CodeObjectVersion);
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Arch/Mips.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- Mips.h - Mips-specific Tool Helpers ----------------------*- C++ -*-===//
//===--- Mips.h - Mips-specific Tool Helpers --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Arch/Sparc.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- Sparc.h - Sparc-specific Tool Helpers ----------------------*- C++ -*-===//
//===--- Sparc.h - Sparc-specific Tool Helpers ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Headers/llvm_libc_wrappers/assert.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===-- Wrapper for C standard assert.h declarations on the GPU ------------===//
//===-- Wrapper for C standard assert.h declarations on the GPU -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4265,6 +4265,8 @@ void Parser::ParseDeclarationSpecifiers(

// constexpr, consteval, constinit specifiers
case tok::kw_constexpr:
if (getLangOpts().C23)
Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, Loc,
PrevSpec, DiagID);
break;
Expand Down
23 changes: 22 additions & 1 deletion clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2984,8 +2984,29 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
OMPDirectiveScope.Exit();
break;
}
case OMPD_declare_target: {
SourceLocation DTLoc = ConsumeAnyToken();
bool HasClauses = Tok.isNot(tok::annot_pragma_openmp_end);
Sema::DeclareTargetContextInfo DTCI(DKind, DTLoc);
if (HasClauses)
ParseOMPDeclareTargetClauses(DTCI);
bool HasImplicitMappings =
!HasClauses || (DTCI.ExplicitlyMapped.empty() && DTCI.Indirect);

if (HasImplicitMappings) {
Diag(Tok, diag::err_omp_unexpected_directive)
<< 1 << getOpenMPDirectiveName(DKind);
SkipUntil(tok::annot_pragma_openmp_end);
break;
}

// Skip the last annot_pragma_openmp_end.
ConsumeAnyToken();

Actions.ActOnFinishedOpenMPDeclareTargetContext(DTCI);
break;
}
case OMPD_declare_simd:
case OMPD_declare_target:
case OMPD_begin_declare_target:
case OMPD_end_declare_target:
case OMPD_requires:
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/AnalysisBasedWarnings.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//=- AnalysisBasedWarnings.cpp - Sema warnings based on libAnalysis -*- C++ -*-=//
//=== AnalysisBasedWarnings.cpp - Sema warnings based on libAnalysis ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Sema/DeclSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,20 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
ThreadStorageClassSpec = TSCS_unspecified;
ThreadStorageClassSpecLoc = SourceLocation();
}
if (S.getLangOpts().C23 &&
getConstexprSpecifier() == ConstexprSpecKind::Constexpr) {
S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
<< DeclSpec::getSpecifierName(getThreadStorageClassSpec())
<< SourceRange(getThreadStorageClassSpecLoc());
}
}

if (S.getLangOpts().C23 &&
getConstexprSpecifier() == ConstexprSpecKind::Constexpr &&
StorageClassSpec == SCS_extern) {
S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
<< DeclSpec::getSpecifierName(getStorageClassSpec())
<< SourceRange(getStorageClassSpecLoc());
}

// If no type specifier was provided and we're parsing a language where
Expand Down
38 changes: 19 additions & 19 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,38 +188,38 @@ const uint64_t Sema::MaximumAlignment;

Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter)
: ExternalSource(nullptr), CurFPFeatures(pp.getLangOpts()),
: CollectStats(false), TUKind(TUKind), CurFPFeatures(pp.getLangOpts()),
LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer),
Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
APINotes(SourceMgr, LangOpts), CollectStats(false),
CodeCompleter(CodeCompleter), CurContext(nullptr),
OriginalLexicalContext(nullptr), MSStructPragmaOn(false),
APINotes(SourceMgr, LangOpts), AnalysisWarnings(*this),
ThreadSafetyDeclCache(nullptr), LateTemplateParser(nullptr),
LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr),
CurContext(nullptr), ExternalSource(nullptr), CurScope(nullptr),
Ident_super(nullptr),
MSPointerToMemberRepresentationMethod(
LangOpts.getMSPointerToMemberRepresentationMethod()),
VtorDispStack(LangOpts.getVtorDispMode()),
MSStructPragmaOn(false), VtorDispStack(LangOpts.getVtorDispMode()),
AlignPackStack(AlignPackInfo(getLangOpts().XLPragmaPack)),
DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
CodeSegStack(nullptr), StrictGuardStackCheckStack(false),
FpPragmaStack(FPOptionsOverride()), CurInitSeg(nullptr),
VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr),
IsBuildingRecoveryCallExpr(false), LateTemplateParser(nullptr),
LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
StdInitializerList(nullptr), StdCoroutineTraitsCache(nullptr),
CXXTypeInfoDecl(nullptr), StdSourceLocationImplDecl(nullptr),
StdCoroutineTraitsCache(nullptr), IdResolver(pp),
OriginalLexicalContext(nullptr), StdInitializerList(nullptr),
FullyCheckedComparisonCategories(
static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
StdSourceLocationImplDecl(nullptr), CXXTypeInfoDecl(nullptr),
GlobalNewDeleteDeclared(false), DisableTypoCorrection(false),
TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context),
NSNumberDecl(nullptr), NSValueDecl(nullptr), NSStringDecl(nullptr),
StringWithUTF8StringMethod(nullptr),
ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
TUKind(TUKind), NumSFINAEErrors(0),
FullyCheckedComparisonCategories(
static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
SatisfactionCache(Context), AccessCheckingSFINAE(false),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr),
DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this),
ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
CurScope(nullptr), Ident_super(nullptr) {
DictionaryWithObjectsMethod(nullptr), CodeCompleter(CodeCompleter),
VarDataSharingAttributesStack(nullptr) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;

Expand Down
24 changes: 13 additions & 11 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5321,6 +5321,9 @@ bool Sema::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
// position of memory order and scope arguments in the builtin
unsigned OrderIndex, ScopeIndex;
switch (BuiltinID) {
case AMDGPU::BI__builtin_amdgcn_get_fpenv:
case AMDGPU::BI__builtin_amdgcn_set_fpenv:
return false;
case AMDGPU::BI__builtin_amdgcn_atomic_inc32:
case AMDGPU::BI__builtin_amdgcn_atomic_inc64:
case AMDGPU::BI__builtin_amdgcn_atomic_dec32:
Expand Down Expand Up @@ -11524,7 +11527,7 @@ void CheckFormatHandler::EmitFormatDiagnostic(
}
}

//===--- CHECK: Printf format string checking ------------------------------===//
//===--- CHECK: Printf format string checking -----------------------------===//

namespace {

Expand Down Expand Up @@ -19090,18 +19093,17 @@ void Sema::DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
LHSExpr = LHSExpr->IgnoreParenImpCasts();
RHSExpr = RHSExpr->IgnoreParenImpCasts();

// Check for a call expression
const CallExpr *CE = dyn_cast<CallExpr>(RHSExpr);
if (!CE || CE->getNumArgs() != 1)
return;

// Check for a call to std::move
if (!CE->isCallToStdMove())
// Check for a call to std::move or for a static_cast<T&&>(..) to an xvalue
// which we can treat as an inlined std::move
if (const auto *CE = dyn_cast<CallExpr>(RHSExpr);
CE && CE->getNumArgs() == 1 && CE->isCallToStdMove())
RHSExpr = CE->getArg(0);
else if (const auto *CXXSCE = dyn_cast<CXXStaticCastExpr>(RHSExpr);
CXXSCE && CXXSCE->isXValue())
RHSExpr = CXXSCE->getSubExpr();
else
return;

// Get argument from std::move
RHSExpr = CE->getArg(0);

const DeclRefExpr *LHSDeclRef = dyn_cast<DeclRefExpr>(LHSExpr);
const DeclRefExpr *RHSDeclRef = dyn_cast<DeclRefExpr>(RHSExpr);

Expand Down
94 changes: 78 additions & 16 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5147,6 +5147,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag)
<< GetDiagnosticTypeSpecifierID(DS)
<< static_cast<int>(DS.getConstexprSpecifier());
else if (getLangOpts().C23)
Diag(DS.getConstexprSpecLoc(), diag::err_c23_constexpr_not_variable);
else
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind)
<< static_cast<int>(DS.getConstexprSpecifier());
Expand Down Expand Up @@ -8649,6 +8651,38 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
return false;
}

static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc,
QualType T) {
QualType CanonT = SemaRef.Context.getCanonicalType(T);
// C23 6.7.1p5: An object declared with storage-class specifier constexpr or
// any of its members, even recursively, shall not have an atomic type, or a
// variably modified type, or a type that is volatile or restrict qualified.
if (CanonT->isVariablyModifiedType()) {
SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
return true;
}

// Arrays are qualified by their element type, so get the base type (this
// works on non-arrays as well).
CanonT = SemaRef.Context.getBaseElementType(CanonT);

if (CanonT->isAtomicType() || CanonT.isVolatileQualified() ||
CanonT.isRestrictQualified()) {
SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
return true;
}

if (CanonT->isRecordType()) {
const RecordDecl *RD = CanonT->getAsRecordDecl();
if (llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) {
return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType());
}))
return true;
}

return false;
}

void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
// If the decl is already known invalid, don't check it.
if (NewVD->isInvalidDecl())
Expand Down Expand Up @@ -8899,6 +8933,12 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
return;
}

if (getLangOpts().C23 && NewVD->isConstexpr() &&
CheckC23ConstexprVarType(*this, NewVD->getLocation(), T)) {
NewVD->setInvalidDecl();
return;
}

if (NewVD->isConstexpr() && !T->isDependentType() &&
RequireLiteralType(NewVD->getLocation(), T,
diag::err_constexpr_var_non_literal)) {
Expand Down Expand Up @@ -9281,6 +9321,22 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
FunctionDecl *NewFD = nullptr;
bool isInline = D.getDeclSpec().isInlineSpecified();

ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
if (ConstexprKind == ConstexprSpecKind::Constinit ||
(SemaRef.getLangOpts().C23 &&
ConstexprKind == ConstexprSpecKind::Constexpr)) {

if (SemaRef.getLangOpts().C23)
SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
diag::err_c23_constexpr_not_variable);
else
SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
diag::err_constexpr_wrong_decl_kind)
<< static_cast<int>(ConstexprKind);
ConstexprKind = ConstexprSpecKind::Unspecified;
D.getMutableDeclSpec().ClearConstexprSpec();
}

if (!SemaRef.getLangOpts().CPlusPlus) {
// Determine whether the function was written with a prototype. This is
// true when:
Expand Down Expand Up @@ -9314,15 +9370,6 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
}

ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier();

ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
if (ConstexprKind == ConstexprSpecKind::Constinit) {
SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
diag::err_constexpr_wrong_decl_kind)
<< static_cast<int>(ConstexprKind);
ConstexprKind = ConstexprSpecKind::Unspecified;
D.getMutableDeclSpec().ClearConstexprSpec();
}
Expr *TrailingRequiresClause = D.getTrailingRequiresClause();

SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R);
Expand Down Expand Up @@ -11432,6 +11479,16 @@ static bool CheckTargetCausesMultiVersioning(Sema &S, FunctionDecl *OldFD,
bool &Redeclaration,
NamedDecl *&OldDecl,
LookupResult &Previous) {
assert(!OldFD->isMultiVersion() && "Unexpected MultiVersion");

// The definitions should be allowed in any order. If we have discovered
// a new target version and the preceeding was the default, then add the
// corresponding attribute to it.
if (OldFD->getMultiVersionKind() == MultiVersionKind::None &&
NewFD->getMultiVersionKind() == MultiVersionKind::TargetVersion)
OldFD->addAttr(TargetVersionAttr::CreateImplicit(S.Context, "default",
OldFD->getSourceRange()));

const auto *NewTA = NewFD->getAttr<TargetAttr>();
const auto *NewTVA = NewFD->getAttr<TargetVersionAttr>();
const auto *OldTA = OldFD->getAttr<TargetAttr>();
Expand All @@ -11458,9 +11515,8 @@ static bool CheckTargetCausesMultiVersioning(Sema &S, FunctionDecl *OldFD,
}

// If this is 'default', permit the forward declaration.
if (!OldFD->isMultiVersion() &&
((NewTA && NewTA->isDefaultVersion() && !OldTA) ||
(NewTVA && NewTVA->isDefaultVersion() && !OldTVA))) {
if ((NewTA && NewTA->isDefaultVersion() && !OldTA) ||
(NewTVA && NewTVA->isDefaultVersion() && !OldTVA)) {
Redeclaration = true;
OldDecl = OldFD;
OldFD->setIsMultiVersion();
Expand Down Expand Up @@ -13909,7 +13965,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
VDecl->setStorageClass(SC_Extern);

// C99 6.7.8p4. All file scoped initializers need to be constant.
if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl())
// Avoid duplicate diagnostics for constexpr variables.
if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
!VDecl->isConstexpr())
CheckForConstantInitializer(Init, DclT);
}

Expand Down Expand Up @@ -14520,17 +14578,21 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
QualType baseType = Context.getBaseElementType(type);
bool HasConstInit = true;

if (getLangOpts().C23 && var->isConstexpr() && !Init)
Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
<< var;

// Check whether the initializer is sufficiently constant.
if (getLangOpts().CPlusPlus && !type->isDependentType() && Init &&
!Init->isValueDependent() &&
if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) &&
!type->isDependentType() && Init && !Init->isValueDependent() &&
(GlobalStorage || var->isConstexpr() ||
var->mightBeUsableInConstantExpressions(Context))) {
// If this variable might have a constant initializer or might be usable in
// constant expressions, check whether or not it actually is now. We can't
// do this lazily, because the result might depend on things that change
// later, such as which constexpr functions happen to be defined.
SmallVector<PartialDiagnosticAt, 8> Notes;
if (!getLangOpts().CPlusPlus11) {
if (!getLangOpts().CPlusPlus11 && !getLangOpts().C23) {
// Prior to C++11, in contexts where a constant initializer is required,
// the set of valid constant initializers is described by syntactic rules
// in [expr.const]p2-6.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaExceptionSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,13 +1017,13 @@ CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
SourceLocation Loc) {
// As an extension, we assume that __attribute__((nothrow)) functions don't
// throw.
if (D && isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
if (isa_and_nonnull<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
return CT_Cannot;

QualType T;

// In C++1z, just look at the function type of the callee.
if (S.getLangOpts().CPlusPlus17 && E && isa<CallExpr>(E)) {
if (S.getLangOpts().CPlusPlus17 && isa_and_nonnull<CallExpr>(E)) {
E = cast<CallExpr>(E)->getCallee();
T = E->getType();
if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) {
Expand Down
119 changes: 115 additions & 4 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,35 @@ static void updateGNUCompoundLiteralRValue(Expr *E) {
}
}

static bool initializingConstexprVariable(const InitializedEntity &Entity) {
Decl *D = Entity.getDecl();
const InitializedEntity *Parent = &Entity;

while (Parent) {
D = Parent->getDecl();
Parent = Parent->getParent();
}

if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr())
return true;

return false;
}

static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
Sema &SemaRef, QualType &TT);

static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
Sema &S) {
Sema &S, bool CheckC23ConstexprInit = false) {
// Get the length of the string as parsed.
auto *ConstantArrayTy =
cast<ConstantArrayType>(Str->getType()->getAsArrayTypeUnsafe());
uint64_t StrLength = ConstantArrayTy->getSize().getZExtValue();

if (CheckC23ConstexprInit)
if (const StringLiteral *SL = dyn_cast<StringLiteral>(Str->IgnoreParens()))
CheckC23ConstexprInitStringLiteral(SL, S, DeclT);

if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) {
// C99 6.7.8p14. We have an array of character type with unknown size
// being initialized to a string literal.
Expand Down Expand Up @@ -1476,7 +1498,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) {
// FIXME: Should we do this checking in verify-only mode?
if (!VerifyOnly)
CheckStringInit(expr, ElemType, arrayType, SemaRef);
CheckStringInit(expr, ElemType, arrayType, SemaRef,
SemaRef.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
if (StructuredList)
UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
++Index;
Expand Down Expand Up @@ -1941,7 +1965,9 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
// constant for each string.
// FIXME: Should we do these checks in verify-only mode too?
if (!VerifyOnly)
CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef);
CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef,
SemaRef.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
if (StructuredList) {
UpdateStructuredListElement(StructuredList, StructuredIndex,
IList->getInit(Index));
Expand Down Expand Up @@ -8377,6 +8403,9 @@ static void DiagnoseNarrowingInInitList(Sema &S,
QualType EntityType,
const Expr *PostInit);

static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
QualType ToType, Expr *Init);

/// Provide warnings when std::move is used on construction.
static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr,
bool IsReturnStmt) {
Expand Down Expand Up @@ -9203,6 +9232,23 @@ ExprResult InitializationSequence::Perform(Sema &S,
return ExprError();
CurInit = CurInitExprRes;

if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) {
CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
CurInit.get());

// C23 6.7.1p6: If an object or subobject declared with storage-class
// specifier constexpr has pointer, integer, or arithmetic type, any
// explicit initializer value for it shall be null, an integer
// constant expression, or an arithmetic constant expression,
// respectively.
Expr::EvalResult ER;
if (Entity.getType()->getAs<PointerType>() &&
CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
!ER.Val.isNullPointer()) {
S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
}
}

bool Complained;
if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
Step->Type, SourceType,
Expand All @@ -9220,7 +9266,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
QualType Ty = Step->Type;
bool UpdateType = ResultType && Entity.getType()->isIncompleteArrayType();
CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty,
S.Context.getAsArrayType(Ty), S);
S.Context.getAsArrayType(Ty), S,
S.getLangOpts().C23 &&
initializingConstexprVariable(Entity));
break;
}

Expand Down Expand Up @@ -10509,6 +10557,69 @@ static void DiagnoseNarrowingInInitList(Sema &S,
S.getLocForEndOfToken(PostInit->getEndLoc()), ")");
}

static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
QualType ToType, Expr *Init) {
assert(S.getLangOpts().C23);
ImplicitConversionSequence ICS = S.TryImplicitConversion(
Init->IgnoreParenImpCasts(), ToType, /*SuppressUserConversions*/ false,
Sema::AllowedExplicit::None,
/*InOverloadResolution*/ false,
/*CStyle*/ false,
/*AllowObjCWritebackConversion=*/false);

if (!ICS.isStandard())
return;

APValue Value;
QualType PreNarrowingType;
// Reuse C++ narrowing check.
switch (ICS.Standard.getNarrowingKind(
S.Context, Init, Value, PreNarrowingType,
/*IgnoreFloatToIntegralConversion*/ false)) {
// The value doesn't fit.
case NK_Constant_Narrowing:
S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
<< Value.getAsString(S.Context, PreNarrowingType) << ToType;
return;

// Conversion to a narrower type.
case NK_Type_Narrowing:
S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
<< ToType << FromType;
return;

// Since we only reuse narrowing check for C23 constexpr variables here, we're
// not really interested in these cases.
case NK_Dependent_Narrowing:
case NK_Variable_Narrowing:
case NK_Not_Narrowing:
return;
}
llvm_unreachable("unhandled case in switch");
}

static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
Sema &SemaRef, QualType &TT) {
assert(SemaRef.getLangOpts().C23);
// character that string literal contains fits into TT - target type.
const ArrayType *AT = SemaRef.Context.getAsArrayType(TT);
QualType CharType = AT->getElementType();
uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
bool isUnsigned = CharType->isUnsignedIntegerType();
llvm::APSInt Value(BitWidth, isUnsigned);
for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
Value = C;
if (Value != C) {
SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I),
diag::err_c23_constexpr_init_not_representable)
<< C << CharType;
return;
}
}
return;
}

//===----------------------------------------------------------------------===//
// Initialization helper functions
//===----------------------------------------------------------------------===//
Expand Down
94 changes: 91 additions & 3 deletions clang/lib/Sema/SemaModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,90 @@ static std::string stringFromPath(ModuleIdPath Path) {
return Name;
}

/// Helper function for makeTransitiveImportsVisible to decide whether
/// the \param Imported module unit is in the same module with the \param
/// CurrentModule.
/// \param FoundPrimaryModuleInterface is a helper parameter to record the
/// primary module interface unit corresponding to the module \param
/// CurrentModule. Since currently it is expensive to decide whether two module
/// units come from the same module by comparing the module name.
static bool
isImportingModuleUnitFromSameModule(Module *Imported, Module *CurrentModule,
Module *&FoundPrimaryModuleInterface) {
if (!Imported->isNamedModule())
return false;

// The a partition unit we're importing must be in the same module of the
// current module.
if (Imported->isModulePartition())
return true;

// If we found the primary module interface during the search process, we can
// return quickly to avoid expensive string comparison.
if (FoundPrimaryModuleInterface)
return Imported == FoundPrimaryModuleInterface;

if (!CurrentModule)
return false;

// Then the imported module must be a primary module interface unit. It
// is only allowed to import the primary module interface unit from the same
// module in the implementation unit and the implementation partition unit.

// Since we'll handle implementation unit above. We can only care
// about the implementation partition unit here.
if (!CurrentModule->isModulePartitionImplementation())
return false;

if (Imported->getPrimaryModuleInterfaceName() ==
CurrentModule->getPrimaryModuleInterfaceName()) {
assert(!FoundPrimaryModuleInterface ||
FoundPrimaryModuleInterface == Imported);
FoundPrimaryModuleInterface = Imported;
return true;
}

return false;
}

/// [module.import]p7:
/// Additionally, when a module-import-declaration in a module unit of some
/// module M imports another module unit U of M, it also imports all
/// translation units imported by non-exported module-import-declarations in
/// the module unit purview of U. These rules can in turn lead to the
/// importation of yet more translation units.
static void
makeTransitiveImportsVisible(VisibleModuleSet &VisibleModules, Module *Imported,
Module *CurrentModule, SourceLocation ImportLoc,
bool IsImportingPrimaryModuleInterface = false) {
assert(Imported->isNamedModule() &&
"'makeTransitiveImportsVisible()' is intended for standard C++ named "
"modules only.");

llvm::SmallVector<Module *, 4> Worklist;
Worklist.push_back(Imported);

Module *FoundPrimaryModuleInterface =
IsImportingPrimaryModuleInterface ? Imported : nullptr;

while (!Worklist.empty()) {
Module *Importing = Worklist.pop_back_val();

if (VisibleModules.isVisible(Importing))
continue;

// FIXME: The ImportLoc here is not meaningful. It may be problematic if we
// use the sourcelocation loaded from the visible modules.
VisibleModules.setVisible(Importing, ImportLoc);

if (isImportingModuleUnitFromSameModule(Importing, CurrentModule,
FoundPrimaryModuleInterface))
for (Module *TransImported : Importing->Imports)
if (!VisibleModules.isVisible(TransImported))
Worklist.push_back(TransImported);
}
}

Sema::DeclGroupPtrTy
Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
// We start in the global module;
Expand Down Expand Up @@ -396,8 +480,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// and return the import decl to be added to the current TU.
if (Interface) {

VisibleModules.setVisible(Interface, ModuleLoc);
VisibleModules.makeTransitiveImportsVisible(Interface, ModuleLoc);
makeTransitiveImportsVisible(VisibleModules, Interface, Mod, ModuleLoc,
/*IsImportingPrimaryModuleInterface=*/true);

// Make the import decl for the interface in the impl module.
ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc,
Expand Down Expand Up @@ -554,7 +638,11 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
if (Mod->isHeaderUnit())
Diag(ImportLoc, diag::warn_experimental_header_unit);

VisibleModules.setVisible(Mod, ImportLoc);
if (Mod->isNamedModule())
makeTransitiveImportsVisible(VisibleModules, Mod, getCurrentModule(),
ImportLoc);
else
VisibleModules.setVisible(Mod, ImportLoc);

checkModuleImportContext(*this, Mod, ImportLoc, CurContext);

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23353,6 +23353,15 @@ void Sema::ActOnOpenMPDeclareTargetName(NamedDecl *ND, SourceLocation Loc,
isa<FunctionTemplateDecl>(ND)) &&
"Expected variable, function or function template.");

if (auto *VD = dyn_cast<VarDecl>(ND)) {
// Only global variables can be marked as declare target.
if (!VD->isFileVarDecl() && !VD->isStaticLocal() &&
!VD->isStaticDataMember()) {
Diag(Loc, diag::err_omp_declare_target_has_local_vars)
<< VD->getNameAsString();
return;
}
}
// Diagnose marking after use as it may lead to incorrect diagnosis and
// codegen.
if (LangOpts.OpenMP >= 50 &&
Expand Down
78 changes: 66 additions & 12 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
NarrowingKind StandardConversionSequence::getNarrowingKind(
ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++");
assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
"narrowing check outside C++");

// C++11 [dcl.init.list]p7:
// A narrowing conversion is an implicit conversion ...
Expand Down Expand Up @@ -414,20 +415,41 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
if (Initializer->isValueDependent())
return NK_Dependent_Narrowing;

if (Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
Expr::EvalResult R;
if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) ||
Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
// Constant!
if (Ctx.getLangOpts().C23)
ConstantValue = R.Val;
assert(ConstantValue.isFloat());
llvm::APFloat FloatVal = ConstantValue.getFloat();
// Convert the source value into the target type.
bool ignored;
llvm::APFloat::opStatus ConvertStatus = FloatVal.convert(
Ctx.getFloatTypeSemantics(ToType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
// If there was no overflow, the source value is within the range of
// values that can be represented.
if (ConvertStatus & llvm::APFloat::opOverflow) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
llvm::APFloat Converted = FloatVal;
llvm::APFloat::opStatus ConvertStatus =
Converted.convert(Ctx.getFloatTypeSemantics(ToType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
Converted.convert(Ctx.getFloatTypeSemantics(FromType),
llvm::APFloat::rmNearestTiesToEven, &ignored);
if (Ctx.getLangOpts().C23) {
if (FloatVal.isNaN() && Converted.isNaN() &&
!FloatVal.isSignaling() && !Converted.isSignaling()) {
// Quiet NaNs are considered the same value, regardless of
// payloads.
return NK_Not_Narrowing;
}
// For normal values, check exact equality.
if (!Converted.bitwiseIsEqual(FloatVal)) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
} else {
// If there was no overflow, the source value is within the range of
// values that can be represented.
if (ConvertStatus & llvm::APFloat::opOverflow) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
}
} else {
return NK_Variable_Narrowing;
Expand Down Expand Up @@ -494,7 +516,30 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
}
return NK_Not_Narrowing;
}
case ICK_Complex_Real:
if (FromType->isComplexType() && !ToType->isComplexType())
return NK_Type_Narrowing;
return NK_Not_Narrowing;

case ICK_Floating_Promotion:
if (Ctx.getLangOpts().C23) {
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
Expr::EvalResult R;
if (Initializer->EvaluateAsRValue(R, Ctx)) {
ConstantValue = R.Val;
assert(ConstantValue.isFloat());
llvm::APFloat FloatVal = ConstantValue.getFloat();
// C23 6.7.3p6 If the initializer has real type and a signaling NaN
// value, the unqualified versions of the type of the initializer and
// the corresponding real type of the object declared shall be
// compatible.
if (FloatVal.isNaN() && FloatVal.isSignaling()) {
ConstantType = Initializer->getType();
return NK_Constant_Narrowing;
}
}
}
return NK_Not_Narrowing;
default:
// Other kinds of conversions are not narrowings.
return NK_Not_Narrowing;
Expand Down Expand Up @@ -10526,14 +10571,23 @@ bool clang::isBetterOverloadCandidate(
// according to the partial ordering rules described in 14.5.5.2, or,
// if not that,
if (Cand1IsSpecialization && Cand2IsSpecialization) {
const auto *Obj1Context =
dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext());
const auto *Obj2Context =
dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext());
if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate(
Cand1.Function->getPrimaryTemplate(),
Cand2.Function->getPrimaryTemplate(), Loc,
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
: TPOC_Call,
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
Cand1.isReversed() ^ Cand2.isReversed()))
Cand1.ExplicitCallArguments,
Obj1Context ? QualType(Obj1Context->getTypeForDecl(), 0)
: QualType{},
Obj2Context ? QualType(Obj2Context->getTypeForDecl(), 0)
: QualType{},
Cand1.isReversed() ^ Cand2.isReversed())) {
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
}
}

// -— F1 and F2 are non-template functions with the same
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2598,7 +2598,7 @@ struct ConvertConstructorToDeductionGuideTransform {
// placeholder to indicate there is a default argument.
QualType ParamTy = NewDI->getType();
NewDefArg = new (SemaRef.Context)
OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(),
OpaqueValueExpr(OldParam->getDefaultArgRange().getBegin(),
ParamTy.getNonLValueExprType(SemaRef.Context),
ParamTy->isLValueReferenceType() ? VK_LValue
: ParamTy->isRValueReferenceType() ? VK_XValue
Expand Down
236 changes: 130 additions & 106 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5333,38 +5333,38 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD,
return false;
}

/// If this is a non-static member function,
static void
AddImplicitObjectParameterType(ASTContext &Context,
CXXMethodDecl *Method,
SmallVectorImpl<QualType> &ArgTypes) {
// C++11 [temp.func.order]p3:
// [...] The new parameter is of type "reference to cv A," where cv are
// the cv-qualifiers of the function template (if any) and A is
// the class of which the function template is a member.
static QualType GetImplicitObjectParameterType(ASTContext &Context,
const CXXMethodDecl *Method,
QualType RawType,
bool IsOtherRvr) {
// C++20 [temp.func.order]p3.1, p3.2:
// - The type X(M) is "rvalue reference to cv A" if the optional
// ref-qualifier of M is && or if M has no ref-qualifier and the
// positionally-corresponding parameter of the other transformed template
// has rvalue reference type; if this determination depends recursively
// upon whether X(M) is an rvalue reference type, it is not considered to
// have rvalue reference type.
//
// The standard doesn't say explicitly, but we pick the appropriate kind of
// reference type based on [over.match.funcs]p4.
assert(Method && Method->isImplicitObjectMemberFunction() &&
"expected an implicit objet function");
QualType ArgTy = Context.getTypeDeclType(Method->getParent());
ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers());
if (Method->getRefQualifier() == RQ_RValue)
ArgTy = Context.getRValueReferenceType(ArgTy);
else
ArgTy = Context.getLValueReferenceType(ArgTy);
ArgTypes.push_back(ArgTy);
// - Otherwise, X(M) is "lvalue reference to cv A".
assert(Method && !Method->isExplicitObjectMemberFunction() &&
"expected a member function with no explicit object parameter");

RawType = Context.getQualifiedType(RawType, Method->getMethodQualifiers());
if (Method->getRefQualifier() == RQ_RValue ||
(IsOtherRvr && Method->getRefQualifier() == RQ_None))
return Context.getRValueReferenceType(RawType);
return Context.getLValueReferenceType(RawType);
}

/// Determine whether the function template \p FT1 is at least as
/// specialized as \p FT2.
static bool isAtLeastAsSpecializedAs(Sema &S,
SourceLocation Loc,
FunctionTemplateDecl *FT1,
FunctionTemplateDecl *FT2,
static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
const FunctionTemplateDecl *FT1,
const FunctionTemplateDecl *FT2,
TemplatePartialOrderingContext TPOC,
unsigned NumCallArguments1,
bool Reversed) {
bool Reversed,
const SmallVector<QualType> &Args1,
const SmallVector<QualType> &Args2) {
assert(!Reversed || TPOC == TPOC_Call);

FunctionDecl *FD1 = FT1->getTemplatedDecl();
Expand All @@ -5381,74 +5381,15 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
// The types used to determine the ordering depend on the context in which
// the partial ordering is done:
TemplateDeductionInfo Info(Loc);
SmallVector<QualType, 4> Args2;
switch (TPOC) {
case TPOC_Call: {
// - In the context of a function call, the function parameter types are
// used.
CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);

// C++11 [temp.func.order]p3:
// [...] If only one of the function templates is a non-static
// member, that function template is considered to have a new
// first parameter inserted in its function parameter list. The
// new parameter is of type "reference to cv A," where cv are
// the cv-qualifiers of the function template (if any) and A is
// the class of which the function template is a member.
//
// Note that we interpret this to mean "if one of the function
// templates is a non-static member and the other is a non-member";
// otherwise, the ordering rules for static functions against non-static
// functions don't make any sense.
//
// C++98/03 doesn't have this provision but we've extended DR532 to cover
// it as wording was broken prior to it.
SmallVector<QualType, 4> Args1;

unsigned NumComparedArguments = NumCallArguments1;

if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method1 against first parameter from Method2.
AddImplicitObjectParameterType(S.Context, Method1, Args1);
++NumComparedArguments;
} else if (!Method1 && Method2 &&
Method2->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method2 against first parameter from Method1.
AddImplicitObjectParameterType(S.Context, Method2, Args2);
} else if (Method1 && Method2 && Reversed &&
Method1->isImplicitObjectMemberFunction() &&
Method2->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method1 against second parameter from Method2
// and 'this' from Method2 against second parameter from Method1.
AddImplicitObjectParameterType(S.Context, Method1, Args1);
AddImplicitObjectParameterType(S.Context, Method2, Args2);
++NumComparedArguments;
}

Args1.insert(Args1.end(), Proto1->param_type_begin(),
Proto1->param_type_end());
Args2.insert(Args2.end(), Proto2->param_type_begin(),
Proto2->param_type_end());

// C++ [temp.func.order]p5:
// The presence of unused ellipsis and default arguments has no effect on
// the partial ordering of function templates.
if (Args1.size() > NumComparedArguments)
Args1.resize(NumComparedArguments);
if (Args2.size() > NumComparedArguments)
Args2.resize(NumComparedArguments);
if (Reversed)
std::reverse(Args2.begin(), Args2.end());

case TPOC_Call:
if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
Args1.data(), Args1.size(), Info, Deduced,
TDF_None, /*PartialOrdering=*/true) !=
TemplateDeductionResult::Success)
return false;

break;
}

case TPOC_Conversion:
// - In the context of a call to a conversion operator, the return types
Expand Down Expand Up @@ -5536,8 +5477,13 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
/// \param NumCallArguments1 The number of arguments in the call to FT1, used
/// only when \c TPOC is \c TPOC_Call.
///
/// \param NumCallArguments2 The number of arguments in the call to FT2, used
/// only when \c TPOC is \c TPOC_Call.
/// \param RawObj1Ty The type of the object parameter of FT1 if a member
/// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
/// template from a member function
///
/// \param RawObj2Ty The type of the object parameter of FT2 if a member
/// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function
/// template from a member function
///
/// \param Reversed If \c true, exactly one of FT1 and FT2 is an overload
/// candidate with a reversed parameter order. In this case, the corresponding
Expand All @@ -5548,13 +5494,76 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
unsigned NumCallArguments2, bool Reversed) {
QualType RawObj1Ty, QualType RawObj2Ty, bool Reversed) {
SmallVector<QualType> Args1;
SmallVector<QualType> Args2;
const FunctionDecl *FD1 = FT1->getTemplatedDecl();
const FunctionDecl *FD2 = FT2->getTemplatedDecl();
bool ShouldConvert1 = false;
bool ShouldConvert2 = false;
QualType Obj1Ty;
QualType Obj2Ty;
if (TPOC == TPOC_Call) {
const FunctionProtoType *Proto1 =
FD1->getType()->getAs<FunctionProtoType>();
const FunctionProtoType *Proto2 =
FD2->getType()->getAs<FunctionProtoType>();

// - In the context of a function call, the function parameter types are
// used.
const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
// C++20 [temp.func.order]p3
// [...] Each function template M that is a member function is
// considered to have a new first parameter of type
// X(M), described below, inserted in its function parameter list.
//
// Note that we interpret "that is a member function" as
// "that is a member function with no expicit object argument".
// Otherwise the ordering rules for methods with expicit objet arguments
// against anything else make no sense.
ShouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
ShouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
if (ShouldConvert1) {
bool IsRValRef2 =
ShouldConvert2
? Method2->getRefQualifier() == RQ_RValue
: Proto2->param_type_begin()[0]->isRValueReferenceType();
// Compare 'this' from Method1 against first parameter from Method2.
Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1, RawObj1Ty,
IsRValRef2);
Args1.push_back(Obj1Ty);
}
if (ShouldConvert2) {
bool IsRValRef1 =
ShouldConvert1
? Method1->getRefQualifier() == RQ_RValue
: Proto1->param_type_begin()[0]->isRValueReferenceType();
// Compare 'this' from Method2 against first parameter from Method1.
Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2, RawObj2Ty,
IsRValRef1);
Args2.push_back(Obj2Ty);
}
size_t NumComparedArguments = NumCallArguments1 + ShouldConvert1;

Args1.insert(Args1.end(), Proto1->param_type_begin(),
Proto1->param_type_end());
Args2.insert(Args2.end(), Proto2->param_type_begin(),
Proto2->param_type_end());

bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
NumCallArguments1, Reversed);
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
NumCallArguments2, Reversed);
// C++ [temp.func.order]p5:
// The presence of unused ellipsis and default arguments has no effect on
// the partial ordering of function templates.
Args1.resize(std::min(Args1.size(), NumComparedArguments));
Args2.resize(std::min(Args2.size(), NumComparedArguments));

if (Reversed)
std::reverse(Args2.begin(), Args2.end());
}
bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, Reversed,
Args1, Args2);
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, Reversed,
Args2, Args1);
// C++ [temp.deduct.partial]p10:
// F is more specialized than G if F is at least as specialized as G and G
// is not at least as specialized as F.
Expand All @@ -5568,12 +5577,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
// ... and if G has a trailing function parameter pack for which F does not
// have a corresponding parameter, and if F does not have a trailing
// function parameter pack, then F is more specialized than G.
FunctionDecl *FD1 = FT1->getTemplatedDecl();
FunctionDecl *FD2 = FT2->getTemplatedDecl();
unsigned NumParams1 = FD1->getNumParams();
unsigned NumParams2 = FD2->getNumParams();
bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();

SmallVector<QualType> Param1;
Param1.reserve(FD1->param_size() + ShouldConvert1);
if (ShouldConvert1)
Param1.push_back(Obj1Ty);
for (const auto &P : FD1->parameters())
Param1.push_back(P->getType());

SmallVector<QualType> Param2;
Param2.reserve(FD2->param_size() + ShouldConvert2);
if (ShouldConvert2)
Param2.push_back(Obj2Ty);
for (const auto &P : FD2->parameters())
Param2.push_back(P->getType());

unsigned NumParams1 = Param1.size();
unsigned NumParams2 = Param2.size();

bool Variadic1 =
FD1->param_size() && FD1->parameters().back()->isParameterPack();
bool Variadic2 =
FD2->param_size() && FD2->parameters().back()->isParameterPack();
if (Variadic1 != Variadic2) {
if (Variadic1 && NumParams1 > NumParams2)
return FT2;
Expand All @@ -5584,8 +5609,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
// there is no wording or even resolution for this issue.
for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType();
QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType();
QualType T1 = Param1[i].getCanonicalType();
QualType T2 = Param2[i].getCanonicalType();
auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
if (!TST1 || !TST2)
Expand Down Expand Up @@ -5644,8 +5669,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
// Any top-level cv-qualifiers modifying a parameter type are deleted when
// forming the function type.
for (unsigned i = 0; i < NumParams1; ++i)
if (!Context.hasSameUnqualifiedType(FD1->getParamDecl(i)->getType(),
FD2->getParamDecl(i)->getType()))
if (!Context.hasSameUnqualifiedType(Param1[i], Param2[i]))
return nullptr;

// C++20 [temp.func.order]p6.3:
Expand Down Expand Up @@ -5733,8 +5757,8 @@ UnresolvedSetIterator Sema::getMostSpecialized(
FunctionTemplateDecl *Challenger
= cast<FunctionDecl>(*I)->getPrimaryTemplate();
assert(Challenger && "Not a function template specialization?");
if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
Loc, TPOC_Other, 0, 0),
if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, Loc,
TPOC_Other, 0),
Challenger)) {
Best = I;
BestTemplate = Challenger;
Expand All @@ -5749,7 +5773,7 @@ UnresolvedSetIterator Sema::getMostSpecialized(
= cast<FunctionDecl>(*I)->getPrimaryTemplate();
if (I != Best &&
!isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
Loc, TPOC_Other, 0, 0),
Loc, TPOC_Other, 0),
BestTemplate)) {
Ambiguous = true;
break;
Expand Down
61 changes: 37 additions & 24 deletions clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,36 @@ namespace ento {
/// checking.
///
/// \sa CheckerContext
class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
check::PostStmt<DeclStmt>,
check::PreObjCMessage,
check::PostObjCMessage,
check::ObjCMessageNil,
check::PreCall,
check::PostCall,
check::BranchCondition,
check::NewAllocator,
check::Location,
check::Bind,
check::DeadSymbols,
check::BeginFunction,
check::EndFunction,
check::EndAnalysis,
check::EndOfTranslationUnit,
eval::Call,
eval::Assume,
check::LiveSymbols,
check::RegionChanges,
check::PointerEscape,
check::ConstPointerEscape,
check::Event<ImplicitNullDerefEvent>,
check::ASTDecl<FunctionDecl> > {
class CheckerDocumentation
: public Checker<
// clang-format off
check::ASTCodeBody,
check::ASTDecl<FunctionDecl>,
check::BeginFunction,
check::Bind,
check::BranchCondition,
check::ConstPointerEscape,
check::DeadSymbols,
check::EndAnalysis,
check::EndFunction,
check::EndOfTranslationUnit,
check::Event<ImplicitNullDerefEvent>,
check::LiveSymbols,
check::Location,
check::NewAllocator,
check::ObjCMessageNil,
check::PointerEscape,
check::PostCall,
check::PostObjCMessage,
check::PostStmt<DeclStmt>,
check::PreCall,
check::PreObjCMessage,
check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
eval::Call
// clang-format on
> {
public:
/// Pre-visit the Statement.
///
Expand Down Expand Up @@ -321,6 +327,13 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
void checkASTDecl(const FunctionDecl *D,
AnalysisManager &Mgr,
BugReporter &BR) const {}

/// Check every declaration that has a statement body in the AST.
///
/// As AST traversal callback, which should only be used when the checker is
/// not path sensitive. It will be called for every Declaration in the AST.
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
BugReporter &BR) const {}
};

void CheckerDocumentation::checkPostStmt(const DeclStmt *DS,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
//===- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
77 changes: 71 additions & 6 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ class MallocChecker
CHECK_FN(checkGMemdup)
CHECK_FN(checkGMallocN)
CHECK_FN(checkGMallocN0)
CHECK_FN(preGetdelim)
CHECK_FN(checkGetdelim)
CHECK_FN(checkReallocN)
CHECK_FN(checkOwnershipAttr)

Expand All @@ -391,6 +393,11 @@ class MallocChecker
using CheckFn = std::function<void(const MallocChecker *,
const CallEvent &Call, CheckerContext &C)>;

const CallDescriptionMap<CheckFn> PreFnMap{
{{{"getline"}, 3}, &MallocChecker::preGetdelim},
{{{"getdelim"}, 4}, &MallocChecker::preGetdelim},
};

const CallDescriptionMap<CheckFn> FreeingMemFnMap{
{{{"free"}, 1}, &MallocChecker::checkFree},
{{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex},
Expand Down Expand Up @@ -439,6 +446,8 @@ class MallocChecker
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
{{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN},
{{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN},
{{{"getline"}, 3}, &MallocChecker::checkGetdelim},
{{{"getdelim"}, 4}, &MallocChecker::checkGetdelim},
};

bool isMemCall(const CallEvent &Call) const;
Expand Down Expand Up @@ -588,11 +597,14 @@ class MallocChecker
/// }
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \param [in] ArgValOpt Optional value to use for the argument instead of
/// the one obtained from ArgExpr.
/// \returns The ProgramState right after deallocation.
[[nodiscard]] ProgramStateRef
FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
AllocationFamily Family, bool ReturnsNullOnFailure = false,
std::optional<SVal> ArgValOpt = {}) const;

// TODO: Needs some refactoring, as all other deallocation modeling
// functions are suffering from out parameters and messy code due to how
Expand Down Expand Up @@ -1423,6 +1435,50 @@ void MallocChecker::checkGMallocN0(const CallEvent &Call,
C.addTransition(State);
}

void MallocChecker::preGetdelim(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;

ProgramStateRef State = C.getState();
const auto LinePtr = getPointeeDefVal(Call.getArgSVal(0), State);
if (!LinePtr)
return;

// FreeMemAux takes IsKnownToBeAllocated as an output parameter, and it will
// be true after the call if the symbol was registered by this checker.
// We do not need this value here, as FreeMemAux will take care
// of reporting any violation of the preconditions.
bool IsKnownToBeAllocated = false;
State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
IsKnownToBeAllocated, AF_Malloc, false, LinePtr);
if (State)
C.addTransition(State);
}

void MallocChecker::checkGetdelim(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;

ProgramStateRef State = C.getState();
// Handle the post-conditions of getline and getdelim:
// Register the new conjured value as an allocated buffer.
const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;

SValBuilder &SVB = C.getSValBuilder();

const auto LinePtr = getPointeeDefVal(Call.getArgSVal(0), State);
const auto Size = getPointeeDefVal(Call.getArgSVal(1), State);
if (!LinePtr || !Size || !LinePtr->getAsRegion())
return;

State = setDynamicExtent(State, LinePtr->getAsRegion(), *Size, SVB);
C.addTransition(MallocUpdateRefState(C, CE, State, AF_Malloc, *LinePtr));
}

void MallocChecker::checkReallocN(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
Expand Down Expand Up @@ -1895,15 +1951,17 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
}
}

ProgramStateRef MallocChecker::FreeMemAux(
CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure) const {
ProgramStateRef
MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
const CallEvent &Call, ProgramStateRef State,
bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure,
std::optional<SVal> ArgValOpt) const {

if (!State)
return nullptr;

SVal ArgVal = C.getSVal(ArgExpr);
SVal ArgVal = ArgValOpt.value_or(C.getSVal(ArgExpr));
if (!isa<DefinedOrUnknownSVal>(ArgVal))
return nullptr;
DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();
Expand Down Expand Up @@ -2881,6 +2939,13 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
return;
}

// We need to handle getline pre-conditions here before the pointed region
// gets invalidated by StreamChecker
if (const auto *PreFN = PreFnMap.lookup(Call)) {
(*PreFN)(this, Call, C);
return;
}

// We will check for double free in the post visit.
if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
const FunctionDecl *FD = FC->getDecl();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
//===- ObjCAutoreleaseWriteChecker.cpp ---------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===-- STLAlgorithmModeling.cpp -----------------------------------*- C++ -*--//
//===-- STLAlgorithmModeling.cpp ----------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//===-- SimpleStreamChecker.cpp -----------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
33 changes: 28 additions & 5 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,18 +318,30 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{{{"fgets"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
{{{"getc"}, 1},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
{{{"fputc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
{{{"fputs"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
{{{"putc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
{{{"fprintf"}},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
{{{"vfprintf"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
{{{"fscanf"}},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
{{{"vfscanf"}, 3},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
{{{"ungetc"}, 2},
{std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
Expand Down Expand Up @@ -389,6 +401,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
mutable int SeekCurVal = 1;
/// Expanded value of SEEK_END, 2 if not found.
mutable int SeekEndVal = 2;
/// The built-in va_list type is platform-specific
mutable QualType VaListType;

void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
Expand Down Expand Up @@ -518,7 +532,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
return nullptr;
for (auto *P : Call.parameters()) {
QualType T = P->getType();
if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
if (!T->isIntegralOrEnumerationType() && !T->isPointerType() &&
T.getCanonicalType() != VaListType)
return nullptr;
}

Expand Down Expand Up @@ -557,6 +572,10 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
SeekCurVal = *OptInt;
}

void initVaListType(CheckerContext &C) const {
VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType();
}

/// Searches for the ExplodedNode where the file descriptor was acquired for
/// StreamSym.
static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
Expand Down Expand Up @@ -705,6 +724,7 @@ static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C,
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
initMacroValues(C);
initVaListType(C);

const FnDescription *Desc = lookupFn(Call);
if (!Desc || !Desc->PreFn)
Expand Down Expand Up @@ -1085,10 +1105,13 @@ void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
if (!StateNotFailed)
return;

SmallVector<unsigned int> EscArgs;
for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
EscArgs.push_back(EscArg);
StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
if (auto const *Callee = Call.getCalleeIdentifier();
!Callee || !Callee->getName().equals("vfscanf")) {
SmallVector<unsigned int> EscArgs;
for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
EscArgs.push_back(EscArg);
StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
}

if (StateNotFailed)
C.addTransition(StateNotFailed);
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,16 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
// previous program state we assuming the newly seen constraint information.
// If we cannot evaluate the condition (and the constraints are the same)
// the analyzer has no information about the value and just assuming it.
// FIXME: This logic is not entirely correct, because e.g. in code like
// void f(unsigned arg) {
// if (arg >= 0) {
// // ...
// }
// }
// it will say that the "arg >= 0" check is _assuming_ something new because
// the constraint that "$arg >= 0" is 1 was added to the list of known
// constraints. However, the unsigned value is always >= 0 so semantically
// this is not a "real" assumption.
bool IsAssuming =
!BRC.getStateManager().haveEqualConstraints(CurrentState, PrevState) ||
CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef();
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
if (MD->isInstance())
if (MD->isImplicitObjectMemberFunction())
return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);

} else if (CE->getCallee()->getType()->isBlockPointerType()) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include <optional>

namespace clang {
Expand Down Expand Up @@ -182,5 +183,13 @@ OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,
}
}

std::optional<DefinedSVal> getPointeeDefVal(SVal PtrSVal,
ProgramStateRef State) {
if (const auto *Ptr = PtrSVal.getAsRegion()) {
return State->getSVal(Ptr).getAs<DefinedSVal>();
}
return std::nullopt;
}

} // namespace ento
} // namespace clang
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3038,7 +3038,7 @@ ProgramStateRef RangeConstraintManager::setRange(ProgramStateRef State,

//===------------------------------------------------------------------------===
// assumeSymX methods: protected interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
//===------------------------------------------------------------------------===

// The syntax for ranges below is mathematical, using [x, y] for closed ranges
// and (x, y) for open ranges. These ranges are modular, corresponding with
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Tooling/Refactoring/AtomicChange.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- AtomicChange.cpp - AtomicChange implementation -----------------*- C++ -*-===//
//===--- AtomicChange.cpp - AtomicChange implementation ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down
5 changes: 5 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ int a2[(intptr_t)&((struct y*)0)->y]; // all-warning {{folded to constant array}
const struct y *yy = (struct y*)0;
const intptr_t L = (intptr_t)(&(yy->y)); // all-error {{not a compile-time constant}}

_Static_assert((long)&((struct y*)0)->y > 0, ""); // pedantic-ref-warning {{GNU extension}} \
// pedantic-ref-note {{this conversion is not allowed in a constant expression}} \
// pedantic-expected-warning {{GNU extension}} \
// pedantic-expected-note {{this conversion is not allowed in a constant expression}}

const ptrdiff_t m = &m + 137 - &m;
_Static_assert(m == 137, ""); // pedantic-ref-warning {{GNU extension}} \
// pedantic-expected-warning {{GNU extension}}
Expand Down
12 changes: 12 additions & 0 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,18 @@ namespace IncDec {
return a[1];
}
static_assert(f() == 3, "");

int nonconst(int a) { // both-note 4{{declared here}}
static_assert(a++, ""); // both-error {{not an integral constant expression}} \
// both-note {{function parameter 'a' with unknown value cannot be used in a constant expression}}
static_assert(a--, ""); // both-error {{not an integral constant expression}} \
// both-note {{function parameter 'a' with unknown value cannot be used in a constant expression}}
static_assert(++a, ""); // both-error {{not an integral constant expression}} \
// both-note {{function parameter 'a' with unknown value cannot be used in a constant expression}}
static_assert(--a, ""); // both-error {{not an integral constant expression}} \
// both-note {{function parameter 'a' with unknown value cannot be used in a constant expression}}
}

};
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t);
void *calloc(size_t, size_t);
void free(void *);
void *alloca(size_t);


#if __OBJC__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// suppressed.
#pragma clang system_header

typedef struct __sFILE {
typedef struct _FILE {
unsigned char *_p;
} FILE;
FILE *fopen(const char *restrict, const char *restrict) __asm("_" "fopen" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define restrict /*restrict*/
#endif

typedef struct _FILE FILE;

typedef __builtin_va_list va_list;

#define va_start(ap, param) __builtin_va_start(ap, param)
Expand All @@ -21,6 +23,10 @@ int vprintf (const char *restrict format, va_list arg);

int vsprintf (char *restrict s, const char *restrict format, va_list arg);

int vfprintf(FILE *stream, const char *format, va_list ap);

int vfscanf(FILE *stream, const char *format, va_list ap);

int some_library_function(int n, va_list arg);

// No warning from system header.
Expand Down
3 changes: 3 additions & 0 deletions clang/test/Analysis/Inputs/system-header-simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ int ferror(FILE *stream);
int fileno(FILE *stream);
int fflush(FILE *stream);


int getc(FILE *stream);

size_t strlen(const char *);

char *strcpy(char *restrict, const char *restrict);
Expand Down
19 changes: 19 additions & 0 deletions clang/test/Analysis/assuming-unsigned-ge-0.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %clang_analyze_cc1 -analyzer-output=text \
// RUN: -analyzer-checker=core -verify %s

int assuming_unsigned_ge_0(unsigned arg) {
// TODO This testcase demonstrates the current incorrect behavior of Clang
// Static Analyzer: here 'arg' is unsigned, so "arg >= 0" is not a fresh
// assumption, but it still appears in the diagnostics as if it's fresh:
// expected-note@+2 {{Assuming 'arg' is >= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg < 0)
return 0;
// expected-note@+2 {{Assuming 'arg' is <= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg > 0)
return 0;
// expected-note@+2 {{Division by zero}}
// expected-warning@+1 {{Division by zero}}
return 100 / arg;
}
11 changes: 11 additions & 0 deletions clang/test/Analysis/cxx2b-deducing-this.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,14 @@ void top() {
s.c();
s.c(11);
}


struct S2 {
bool operator==(this auto, S2) {
return true;
}
};
void use_deducing_this() {
int result = S2{} == S2{}; // no-crash
clang_analyzer_dump(result); // expected-warning {{1 S32b}}
}
95 changes: 95 additions & 0 deletions clang/test/Analysis/getline-alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s

// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,alpha.unix,debug.ExprInspection -verify %s

#include "Inputs/system-header-simulator.h"
#include "Inputs/system-header-simulator-for-malloc.h"

void test_getline_null_buffer() {
FILE *F1 = tmpfile();
if (!F1)
return;
char *buffer = NULL;
size_t n = 0;
if (getline(&buffer, &n, F1) > 0) {
char c = buffer[0]; // ok
}
free(buffer);
fclose(F1);
}

void test_getline_malloc_buffer() {
FILE *F1 = tmpfile();
if (!F1)
return;

size_t n = 10;
char *buffer = malloc(n);
char *ptr = buffer;

ssize_t r = getdelim(&buffer, &n, '\r', F1);
// ptr may be dangling
free(ptr); // expected-warning {{Attempt to free released memory}}
free(buffer); // ok
fclose(F1);
}

void test_getline_alloca() {
FILE *F1 = tmpfile();
if (!F1)
return;
size_t n = 10;
char *buffer = alloca(n);
getline(&buffer, &n, F1); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
fclose(F1);
}

void test_getline_invalid_ptr() {
FILE *F1 = tmpfile();
if (!F1)
return;
size_t n = 10;
char *buffer = (char*)test_getline_invalid_ptr;
getline(&buffer, &n, F1); // expected-warning {{Argument to getline() is the address of the function 'test_getline_invalid_ptr', which is not memory allocated by malloc()}}
fclose(F1);
}

void test_getline_leak() {
FILE *F1 = tmpfile();
if (!F1)
return;

char *buffer = NULL;
size_t n = 0;
ssize_t read;

while ((read = getline(&buffer, &n, F1)) != -1) {
printf("%s\n", buffer);
}

fclose(F1); // expected-warning {{Potential memory leak}}
}

void test_getline_stack() {
size_t n = 10;
char buffer[10];
char *ptr = buffer;

FILE *F1 = tmpfile();
if (!F1)
return;

getline(&ptr, &n, F1); // expected-warning {{Argument to getline() is the address of the local variable 'buffer', which is not memory allocated by malloc()}}
}

void test_getline_static() {
static size_t n = 10;
static char buffer[10];
char *ptr = buffer;

FILE *F1 = tmpfile();
if (!F1)
return;

getline(&ptr, &n, F1); // expected-warning {{Argument to getline() is the address of the static variable 'buffer', which is not memory allocated by malloc()}}
}
4 changes: 2 additions & 2 deletions clang/test/Analysis/misc-ps-region-store.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin9 -analyzer-checker=core,alpha.core -verify -fblocks %s
// expected-no-diagnostics

//===------------------------------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// This files tests our path-sensitive handling of Objective-c++ files.
//===------------------------------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//

// Test basic handling of references.
char &test1_aux();
Expand Down
42 changes: 42 additions & 0 deletions clang/test/Analysis/stream-invalidate.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// RUN: -analyzer-checker=debug.ExprInspection

#include "Inputs/system-header-simulator.h"
#include "Inputs/system-header-simulator-for-valist.h"

void clang_analyzer_eval(int);
void clang_analyzer_dump(int);
Expand Down Expand Up @@ -145,3 +146,44 @@ void test_fgetpos() {

fclose(F);
}

void test_fprintf() {
FILE *F1 = tmpfile();
if (!F1)
return;

unsigned a = 42;
char *output = "HELLO";
int r = fprintf(F1, "%s\t%u\n", output, a);
// fprintf does not invalidate any of its input
// 69 is ascii for 'E'
clang_analyzer_dump(a); // expected-warning {{42 S32b}}
clang_analyzer_dump(output[1]); // expected-warning {{69 S32b}}
fclose(F1);
}

int test_vfscanf_inner(const char *fmt, ...) {
FILE *F1 = tmpfile();
if (!F1)
return EOF;

va_list ap;
va_start(ap, fmt);

int r = vfscanf(F1, fmt, ap);

fclose(F1);
va_end(ap);
return r;
}

void test_vfscanf() {
int i = 42;
int j = 43;
int r = test_vfscanf_inner("%d", &i);
if (r != EOF) {
// i gets invalidated by the call to test_vfscanf_inner, not by vfscanf.
clang_analyzer_dump(i); // expected-warning {{conj_$}}
clang_analyzer_dump(j); // expected-warning {{43 S32b}}
}
}
39 changes: 38 additions & 1 deletion clang/test/Analysis/stream.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection -verify %s

#include "Inputs/system-header-simulator.h"
#include "Inputs/system-header-simulator-for-valist.h"

void clang_analyzer_eval(int);

Expand Down Expand Up @@ -65,12 +69,24 @@ void check_fseek(void) {
fclose(fp);
}

void check_fseeko(void) {
FILE *fp = tmpfile();
fseeko(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}

void check_ftell(void) {
FILE *fp = tmpfile();
ftell(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}

void check_ftello(void) {
FILE *fp = tmpfile();
ftello(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}

void check_rewind(void) {
FILE *fp = tmpfile();
rewind(fp); // expected-warning {{Stream pointer might be NULL}}
Expand Down Expand Up @@ -129,6 +145,18 @@ void f_dopen(int fd) {
fclose(F);
}

void f_vfprintf(int fd, va_list args) {
FILE *F = fdopen(fd, "r");
vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}}
fclose(F);
}

void f_vfscanf(int fd, va_list args) {
FILE *F = fdopen(fd, "r");
vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}}
fclose(F);
}

void f_seek(void) {
FILE *p = fopen("foo", "r");
if (!p)
Expand All @@ -138,6 +166,15 @@ void f_seek(void) {
fclose(p);
}

void f_seeko(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fseeko(p, 1, SEEK_SET); // no-warning
fseeko(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}}
fclose(p);
}

void f_double_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
Expand Down
Loading