92 changes: 71 additions & 21 deletions clang/lib/CodeGen/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,22 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {

void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
const CallArgList &Args) const override;
const FunctionDecl *Callee, const CallArgList &Args,
QualType ReturnType) const override;

private:
// Diagnose calls between functions with incompatible Streaming SVE
// attributes.
void checkFunctionCallABIStreaming(CodeGenModule &CGM, SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee) const;
// Diagnose calls which must pass arguments in floating-point registers when
// the selected target does not have floating-point registers.
void checkFunctionCallABISoftFloat(CodeGenModule &CGM, SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
const CallArgList &Args,
QualType ReturnType) const;
};

class WindowsAArch64TargetCodeGenInfo : public AArch64TargetCodeGenInfo {
Expand Down Expand Up @@ -853,37 +867,42 @@ static bool isStreamingCompatible(const FunctionDecl *F) {
return false;
}

// Report an error if an argument or return value of type Ty would need to be
// passed in a floating-point register.
static void diagnoseIfNeedsFPReg(DiagnosticsEngine &Diags,
const StringRef ABIName,
const AArch64ABIInfo &ABIInfo,
const QualType &Ty, const NamedDecl *D) {
const Type *HABase = nullptr;
uint64_t HAMembers = 0;
if (Ty->isFloatingType() || Ty->isVectorType() ||
ABIInfo.isHomogeneousAggregate(Ty, HABase, HAMembers)) {
Diags.Report(D->getLocation(), diag::err_target_unsupported_type_for_abi)
<< D->getDeclName() << Ty << ABIName;
}
}

// If we are using a hard-float ABI, but do not have floating point registers,
// then report an error for any function arguments or returns which would be
// passed in floating-pint registers.
void AArch64TargetCodeGenInfo::checkFunctionABI(
CodeGenModule &CGM, const FunctionDecl *FuncDecl) const {
const AArch64ABIInfo &ABIInfo = getABIInfo<AArch64ABIInfo>();
const TargetInfo &TI = ABIInfo.getContext().getTargetInfo();

// If we are using a hard-float ABI, but do not have floating point
// registers, then report an error for any function arguments or returns
// which would be passed in floating-pint registers.
auto CheckType = [&CGM, &TI, &ABIInfo](const QualType &Ty,
const NamedDecl *D) {
const Type *HABase = nullptr;
uint64_t HAMembers = 0;
if (Ty->isFloatingType() || Ty->isVectorType() ||
ABIInfo.isHomogeneousAggregate(Ty, HABase, HAMembers)) {
CGM.getDiags().Report(D->getLocation(),
diag::err_target_unsupported_type_for_abi)
<< D->getDeclName() << Ty << TI.getABI();
}
};

if (!TI.hasFeature("fp") && !ABIInfo.isSoftFloat()) {
CheckType(FuncDecl->getReturnType(), FuncDecl);
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo,
FuncDecl->getReturnType(), FuncDecl);
for (ParmVarDecl *PVD : FuncDecl->parameters()) {
CheckType(PVD->getType(), PVD);
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, PVD->getType(),
PVD);
}
}
}

void AArch64TargetCodeGenInfo::checkFunctionCallABI(
void AArch64TargetCodeGenInfo::checkFunctionCallABIStreaming(
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
const FunctionDecl *Callee, const CallArgList &Args) const {
const FunctionDecl *Callee) const {
if (!Caller || !Callee || !Callee->hasAttr<AlwaysInlineAttr>())
return;

Expand All @@ -903,6 +922,37 @@ void AArch64TargetCodeGenInfo::checkFunctionCallABI(
<< Callee->getDeclName();
}

// If the target does not have floating-point registers, but we are using a
// hard-float ABI, there is no way to pass floating-point, vector or HFA values
// to functions, so we report an error.
void AArch64TargetCodeGenInfo::checkFunctionCallABISoftFloat(
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
const FunctionDecl *Callee, const CallArgList &Args,
QualType ReturnType) const {
const AArch64ABIInfo &ABIInfo = getABIInfo<AArch64ABIInfo>();
const TargetInfo &TI = ABIInfo.getContext().getTargetInfo();

if (!Caller || TI.hasFeature("fp") || ABIInfo.isSoftFloat())
return;

diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, ReturnType,
Caller);

for (const CallArg &Arg : Args)
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, Arg.getType(),
Caller);
}

void AArch64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
const CallArgList &Args,
QualType ReturnType) const {
checkFunctionCallABIStreaming(CGM, CallLoc, Caller, Callee);
checkFunctionCallABISoftFloat(CGM, CallLoc, Caller, Callee, Args, ReturnType);
}

void AArch64ABIInfo::appendAttributeMangling(TargetClonesAttr *Attr,
unsigned Index,
raw_ostream &Out) const {
Expand Down
16 changes: 11 additions & 5 deletions clang/lib/CodeGen/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1482,8 +1482,8 @@ class X86_64TargetCodeGenInfo : public TargetCodeGenInfo {

void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
const CallArgList &Args) const override;
const FunctionDecl *Callee, const CallArgList &Args,
QualType ReturnType) const override;
};
} // namespace

Expand Down Expand Up @@ -1558,9 +1558,15 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
return false;
}

void X86_64TargetCodeGenInfo::checkFunctionCallABI(
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
const FunctionDecl *Callee, const CallArgList &Args) const {
void X86_64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
const CallArgList &Args,
QualType ReturnType) const {
if (!Callee)
return;

llvm::StringMap<bool> CallerMap;
llvm::StringMap<bool> CalleeMap;
unsigned ArgIndex = 0;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ struct MappingTraits<FormatStyle::ShortCaseStatementsAlignmentStyle> {
IO.mapOptional("Enabled", Value.Enabled);
IO.mapOptional("AcrossEmptyLines", Value.AcrossEmptyLines);
IO.mapOptional("AcrossComments", Value.AcrossComments);
IO.mapOptional("AlignCaseArrows", Value.AlignCaseArrows);
IO.mapOptional("AlignCaseColons", Value.AlignCaseColons);
}
};
Expand Down Expand Up @@ -911,6 +912,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.AllowBreakBeforeNoexceptSpecifier);
IO.mapOptional("AllowShortBlocksOnASingleLine",
Style.AllowShortBlocksOnASingleLine);
IO.mapOptional("AllowShortCaseExpressionOnASingleLine",
Style.AllowShortCaseExpressionOnASingleLine);
IO.mapOptional("AllowShortCaseLabelsOnASingleLine",
Style.AllowShortCaseLabelsOnASingleLine);
IO.mapOptional("AllowShortCompoundRequirementOnASingleLine",
Expand Down Expand Up @@ -1423,6 +1426,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true;
LLVMStyle.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Never;
LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never;
LLVMStyle.AllowShortCaseExpressionOnASingleLine = true;
LLVMStyle.AllowShortCaseLabelsOnASingleLine = false;
LLVMStyle.AllowShortCompoundRequirementOnASingleLine = true;
LLVMStyle.AllowShortEnumsOnASingleLine = true;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace format {
/* l_brace of a block that is not the body of a (e.g. loop) statement. */ \
TYPE(BlockLBrace) \
TYPE(BracedListLBrace) \
TYPE(CaseLabelArrow) \
/* The colon at the end of a case label. */ \
TYPE(CaseLabelColon) \
TYPE(CastRParen) \
Expand Down Expand Up @@ -148,6 +149,8 @@ namespace format {
TYPE(StructLBrace) \
TYPE(StructRBrace) \
TYPE(StructuredBindingLSquare) \
TYPE(SwitchExpressionLabel) \
TYPE(SwitchExpressionLBrace) \
TYPE(TableGenBangOperator) \
TYPE(TableGenCondOperator) \
TYPE(TableGenCondOperatorColon) \
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5051,6 +5051,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
return true; // "x! as string", "x! in y"
}
} else if (Style.Language == FormatStyle::LK_Java) {
if (Left.is(TT_CaseLabelArrow) || Right.is(TT_CaseLabelArrow))
return true;
if (Left.is(tok::r_square) && Right.is(tok::l_brace))
return true;
// spaces inside square brackets.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Format/UnwrappedLineFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,12 @@ class LineJoiner {
}
}

if (TheLine->First->is(TT_SwitchExpressionLabel)) {
return Style.AllowShortCaseExpressionOnASingleLine
? tryMergeShortCaseLabels(I, E, Limit)
: 0;
}

if (TheLine->Last->is(tok::l_brace)) {
bool ShouldMerge = false;
// Try to merge records.
Expand Down
46 changes: 37 additions & 9 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,9 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
unsigned StoredPosition = Tokens->getPosition();
auto *Next = Tokens->getNextNonComment();
FormatTok = Tokens->setPosition(StoredPosition);
if (Next->isNot(tok::colon)) {
// default not followed by ':' is not a case label; treat it like
// an identifier.
if (!Next->isOneOf(tok::colon, tok::arrow)) {
// default not followed by `:` or `->` is not a case label; treat it
// like an identifier.
parseStructuralElement();
break;
}
Expand All @@ -451,6 +451,7 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
}
if (!SwitchLabelEncountered &&
(Style.IndentCaseLabels ||
(OpeningBrace && OpeningBrace->is(TT_SwitchExpressionLBrace)) ||
(Line->InPPDirective && Line->Level == 1))) {
++Line->Level;
}
Expand Down Expand Up @@ -1519,24 +1520,32 @@ void UnwrappedLineParser::parseStructuralElement(
// 'switch: string' field declaration.
break;
}
parseSwitch();
parseSwitch(/*IsExpr=*/false);
return;
case tok::kw_default:
case tok::kw_default: {
// In Verilog default along with other labels are handled in the next loop.
if (Style.isVerilog())
break;
if (Style.isJavaScript() && Line->MustBeDeclaration) {
// 'default: string' field declaration.
break;
}
auto *Default = FormatTok;
nextToken();
if (FormatTok->is(tok::colon)) {
FormatTok->setFinalizedType(TT_CaseLabelColon);
parseLabel();
return;
}
if (FormatTok->is(tok::arrow)) {
FormatTok->setFinalizedType(TT_CaseLabelArrow);
Default->setFinalizedType(TT_SwitchExpressionLabel);
parseLabel();
return;
}
// e.g. "default void f() {}" in a Java interface.
break;
}
case tok::kw_case:
// Proto: there are no switch/case statements.
if (Style.Language == FormatStyle::LK_Proto) {
Expand Down Expand Up @@ -2062,6 +2071,11 @@ void UnwrappedLineParser::parseStructuralElement(
case tok::kw_new:
parseNew();
break;
case tok::kw_switch:
if (Style.Language == FormatStyle::LK_Java)
parseSwitch(/*IsExpr=*/true);
nextToken();
break;
case tok::kw_case:
// Proto: there are no switch/case statements.
if (Style.Language == FormatStyle::LK_Proto) {
Expand Down Expand Up @@ -2589,6 +2603,9 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
else
nextToken();
break;
case tok::kw_switch:
parseSwitch(/*IsExpr=*/true);
break;
case tok::kw_requires: {
auto RequiresToken = FormatTok;
nextToken();
Expand Down Expand Up @@ -3246,6 +3263,7 @@ void UnwrappedLineParser::parseLabel(bool LeftAlignLabel) {

void UnwrappedLineParser::parseCaseLabel() {
assert(FormatTok->is(tok::kw_case) && "'case' expected");
auto *Case = FormatTok;

// FIXME: fix handling of complex expressions here.
do {
Expand All @@ -3254,11 +3272,16 @@ void UnwrappedLineParser::parseCaseLabel() {
FormatTok->setFinalizedType(TT_CaseLabelColon);
break;
}
if (Style.Language == FormatStyle::LK_Java && FormatTok->is(tok::arrow)) {
FormatTok->setFinalizedType(TT_CaseLabelArrow);
Case->setFinalizedType(TT_SwitchExpressionLabel);
break;
}
} while (!eof());
parseLabel();
}

void UnwrappedLineParser::parseSwitch() {
void UnwrappedLineParser::parseSwitch(bool IsExpr) {
assert(FormatTok->is(tok::kw_switch) && "'switch' expected");
nextToken();
if (FormatTok->is(tok::l_paren))
Expand All @@ -3268,10 +3291,15 @@ void UnwrappedLineParser::parseSwitch() {

if (FormatTok->is(tok::l_brace)) {
CompoundStatementIndenter Indenter(this, Style, Line->Level);
FormatTok->setFinalizedType(TT_ControlStatementLBrace);
parseBlock();
FormatTok->setFinalizedType(IsExpr ? TT_SwitchExpressionLBrace
: TT_ControlStatementLBrace);
if (IsExpr)
parseChildBlock();
else
parseBlock();
setPreviousRBraceType(TT_ControlStatementRBrace);
addUnwrappedLine();
if (!IsExpr)
addUnwrappedLine();
} else {
addUnwrappedLine();
++Line->Level;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/UnwrappedLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class UnwrappedLineParser {
void parseDoWhile();
void parseLabel(bool LeftAlignLabel = false);
void parseCaseLabel();
void parseSwitch();
void parseSwitch(bool IsExpr);
void parseNamespace();
bool parseModuleImport();
void parseNew();
Expand Down
22 changes: 14 additions & 8 deletions clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));
calculateLineBreakInformation();
alignConsecutiveMacros();
alignConsecutiveShortCaseStatements();
alignConsecutiveShortCaseStatements(/*IsExpr=*/true);
alignConsecutiveShortCaseStatements(/*IsExpr=*/false);
alignConsecutiveDeclarations();
alignConsecutiveBitFields();
alignConsecutiveAssignments();
Expand Down Expand Up @@ -878,22 +879,27 @@ void WhitespaceManager::alignConsecutiveColons(
Changes, /*StartAt=*/0, AlignStyle);
}

void WhitespaceManager::alignConsecutiveShortCaseStatements() {
void WhitespaceManager::alignConsecutiveShortCaseStatements(bool IsExpr) {
if (!Style.AlignConsecutiveShortCaseStatements.Enabled ||
!Style.AllowShortCaseLabelsOnASingleLine) {
!(IsExpr ? Style.AllowShortCaseExpressionOnASingleLine
: Style.AllowShortCaseLabelsOnASingleLine)) {
return;
}

const auto Type = IsExpr ? TT_CaseLabelArrow : TT_CaseLabelColon;
const auto &Option = Style.AlignConsecutiveShortCaseStatements;
const bool AlignArrowOrColon =
IsExpr ? Option.AlignCaseArrows : Option.AlignCaseColons;

auto Matches = [&](const Change &C) {
if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons)
return C.Tok->is(TT_CaseLabelColon);
if (AlignArrowOrColon)
return C.Tok->is(Type);

// Ignore 'IsInsideToken' to allow matching trailing comments which
// need to be reflowed as that causes the token to appear in two
// different changes, which will cause incorrect alignment as we'll
// reflow early due to detecting multiple aligning tokens per line.
return !C.IsInsideToken && C.Tok->Previous &&
C.Tok->Previous->is(TT_CaseLabelColon);
return !C.IsInsideToken && C.Tok->Previous && C.Tok->Previous->is(Type);
};

unsigned MinColumn = 0;
Expand Down Expand Up @@ -944,7 +950,7 @@ void WhitespaceManager::alignConsecutiveShortCaseStatements() {
if (Changes[I].Tok->isNot(tok::comment))
LineIsComment = false;

if (Changes[I].Tok->is(TT_CaseLabelColon)) {
if (Changes[I].Tok->is(Type)) {
LineIsEmptyCase =
!Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment();

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/WhitespaceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class WhitespaceManager {
void alignChainedConditionals();

/// Align consecutive short case statements over all \c Changes.
void alignConsecutiveShortCaseStatements();
void alignConsecutiveShortCaseStatements(bool IsExpr);

/// Align consecutive TableGen DAGArg colon over all \c Changes.
void alignConsecutiveTableGenBreakingDAGArgColons();
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Headers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ set(core_files
stdarg.h
__stdarg___gnuc_va_list.h
__stdarg___va_copy.h
__stdarg_header_macro.h
__stdarg_va_arg.h
__stdarg_va_copy.h
__stdarg_va_list.h
stdatomic.h
stdbool.h
stdckdint.h
stddef.h
__stddef_header_macro.h
__stddef_max_align_t.h
__stddef_null.h
__stddef_nullptr_t.h
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Headers/__stdarg_header_macro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*===---- __stdarg_header_macro.h ------------------------------------------===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===-----------------------------------------------------------------------===
*/

#ifndef __STDARG_H
#define __STDARG_H
#endif
12 changes: 12 additions & 0 deletions clang/lib/Headers/__stddef_header_macro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*===---- __stddef_header_macro.h ------------------------------------------===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===-----------------------------------------------------------------------===
*/

#ifndef __STDDEF_H
#define __STDDEF_H
#endif
2 changes: 1 addition & 1 deletion clang/lib/Headers/arm_acle.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ __swp(uint32_t __x, volatile uint32_t *__p) {
#endif

/* 7.7 NOP */
#if !defined(_MSC_VER) || !defined(__aarch64__)
#if !defined(_MSC_VER) || (!defined(__aarch64__) && !defined(__arm64ec__))
static __inline__ void __attribute__((__always_inline__, __nodebug__)) __nop(void) {
__builtin_arm_nop();
}
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Headers/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ module _Builtin_stdarg [system] {
export *
}

explicit module header_macro {
header "__stdarg_header_macro.h"
export *
}

explicit module va_arg {
header "__stdarg_va_arg.h"
export *
Expand Down Expand Up @@ -232,6 +237,10 @@ module _Builtin_stdbool [system] {
module _Builtin_stddef [system] {
textual header "stddef.h"

explicit module header_macro {
header "__stddef_header_macro.h"
export *
}
// __stddef_max_align_t.h is always in this module, even if
// -fbuiltin-headers-in-system-modules is passed.
explicit module max_align_t {
Expand Down
28 changes: 6 additions & 22 deletions clang/lib/Headers/stdarg.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,13 @@
* need to use some of its interfaces. Otherwise this header provides all of
* the expected interfaces.
*
* When clang modules are enabled, this header is a textual header. It ignores
* its header guard so that multiple submodules can export its interfaces.
* Take module SM with submodules A and B, whose headers both include stdarg.h
* When SM.A builds, __STDARG_H will be defined. When SM.B builds, the
* definition from SM.A will leak when building without local submodule
* visibility. stdarg.h wouldn't include any of its implementation headers, and
* SM.B wouldn't import any of the stdarg modules, and SM.B's `export *`
* wouldn't export any stdarg interfaces as expected. However, since stdarg.h
* ignores its header guard when building with modules, it all works as
* expected.
*
* When clang modules are not enabled, the header guards can function in the
* normal simple fashion.
* When clang modules are enabled, this header is a textual header to support
* the multiple include behavior. As such, it doesn't directly declare anything
* so that it doesn't add duplicate declarations to all of its includers'
* modules.
*/
#if !defined(__STDARG_H) || __has_feature(modules) || \
defined(__need___va_list) || defined(__need_va_list) || \
defined(__need_va_arg) || defined(__need___va_copy) || \
defined(__need_va_copy)

#if defined(__MVS__) && __has_include_next(<stdarg.h>)
#define __STDARG_H
#include <__stdarg_header_macro.h>
#undef __need___va_list
#undef __need_va_list
#undef __need_va_arg
Expand All @@ -46,7 +32,7 @@
#if !defined(__need___va_list) && !defined(__need_va_list) && \
!defined(__need_va_arg) && !defined(__need___va_copy) && \
!defined(__need_va_copy)
#define __STDARG_H
#include <__stdarg_header_macro.h>
#define __need___va_list
#define __need_va_list
#define __need_va_arg
Expand Down Expand Up @@ -87,5 +73,3 @@
#endif /* defined(__need_va_copy) */

#endif /* __MVS__ */

#endif
30 changes: 6 additions & 24 deletions clang/lib/Headers/stddef.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,13 @@
* need to use some of its interfaces. Otherwise this header provides all of
* the expected interfaces.
*
* When clang modules are enabled, this header is a textual header. It ignores
* its header guard so that multiple submodules can export its interfaces.
* Take module SM with submodules A and B, whose headers both include stddef.h
* When SM.A builds, __STDDEF_H will be defined. When SM.B builds, the
* definition from SM.A will leak when building without local submodule
* visibility. stddef.h wouldn't include any of its implementation headers, and
* SM.B wouldn't import any of the stddef modules, and SM.B's `export *`
* wouldn't export any stddef interfaces as expected. However, since stddef.h
* ignores its header guard when building with modules, it all works as
* expected.
*
* When clang modules are not enabled, the header guards can function in the
* normal simple fashion.
* When clang modules are enabled, this header is a textual header to support
* the multiple include behavior. As such, it doesn't directly declare anything
* so that it doesn't add duplicate declarations to all of its includers'
* modules.
*/
#if !defined(__STDDEF_H) || __has_feature(modules) || \
(defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1) || \
defined(__need_ptrdiff_t) || defined(__need_size_t) || \
defined(__need_rsize_t) || defined(__need_wchar_t) || \
defined(__need_NULL) || defined(__need_nullptr_t) || \
defined(__need_unreachable) || defined(__need_max_align_t) || \
defined(__need_offsetof) || defined(__need_wint_t)

#if defined(__MVS__) && __has_include_next(<stddef.h>)
#define __STDDEF_H
#include <__stddef_header_macro.h>
#undef __need_ptrdiff_t
#undef __need_size_t
#undef __need_rsize_t
Expand All @@ -57,7 +40,7 @@
!defined(__need_NULL) && !defined(__need_nullptr_t) && \
!defined(__need_unreachable) && !defined(__need_max_align_t) && \
!defined(__need_offsetof) && !defined(__need_wint_t)
#define __STDDEF_H
#include <__stddef_header_macro.h>
#define __need_ptrdiff_t
#define __need_size_t
/* ISO9899:2011 7.20 (C11 Annex K): Define rsize_t if __STDC_WANT_LIB_EXT1__ is
Expand Down Expand Up @@ -137,4 +120,3 @@ __WINT_TYPE__ directly; accommodate both by requiring __need_wint_t */
#endif /* __need_wint_t */

#endif /* __MVS__ */
#endif
7 changes: 5 additions & 2 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13530,9 +13530,12 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
}

if (VDecl->isInvalidDecl()) {
CorrectDelayedTyposInExpr(Init, VDecl);
ExprResult Res = CorrectDelayedTyposInExpr(Init, VDecl);
SmallVector<Expr *> SubExprs;
if (Res.isUsable())
SubExprs.push_back(Res.get());
ExprResult Recovery =
CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), {Init});
CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), SubExprs);
if (Expr *E = Recovery.get())
VDecl->setInit(E);
return;
Expand Down
452 changes: 205 additions & 247 deletions clang/lib/Sema/SemaTemplate.cpp

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,31 @@ ASTFileSignature ASTWriter::createSignatureForNamedModule() const {
for (auto [ExportImported, _] : WritingModule->Exports)
Hasher.update(ExportImported->Signature);

// We combine all the used modules to make sure the signature is precise.
// Consider the case like:
//
// // a.cppm
// export module a;
// export inline int a() { ... }
//
// // b.cppm
// export module b;
// import a;
// export inline int b() { return a(); }
//
// Since both `a()` and `b()` are inline, we need to make sure the BMI of
// `b.pcm` will change after the implementation of `a()` changes. We can't
// get that naturally since we won't record the body of `a()` during the
// writing process. We can't reuse ODRHash here since ODRHash won't calculate
// the called function recursively. So ODRHash will be problematic if `a()`
// calls other inline functions.
//
// Probably we can solve this by a new hash mechanism. But the safety and
// efficiency may a problem too. Here we just combine the hash value of the
// used modules conservatively.
for (Module *M : TouchedTopLevelModules)
Hasher.update(M->Signature);

return ASTFileSignature::create(Hasher.result());
}

Expand Down Expand Up @@ -6112,8 +6137,12 @@ LocalDeclID ASTWriter::GetDeclRef(const Decl *D) {

// If D comes from an AST file, its declaration ID is already known and
// fixed.
if (D->isFromASTFile())
if (D->isFromASTFile()) {
if (isWritingStdCXXNamedModules() && D->getOwningModule())
TouchedTopLevelModules.insert(D->getOwningModule()->getTopLevelModule());

return LocalDeclID(D->getGlobalID());
}

assert(!(reinterpret_cast<uintptr_t>(D) & 0x01) && "Invalid decl pointer");
LocalDeclID &ID = DeclIDs[D];
Expand Down
40 changes: 20 additions & 20 deletions clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,27 +148,28 @@ using MutexDescriptor =
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
private:
const std::array<MutexDescriptor, 8> MutexDescriptors{
MemberMutexDescriptor(
CallDescription(/*QualifiedName=*/{"std", "mutex", "lock"},
/*RequiredArgs=*/0),
CallDescription({"std", "mutex", "unlock"}, 0)),
FirstArgMutexDescriptor(CallDescription({"pthread_mutex_lock"}, 1),
CallDescription({"pthread_mutex_unlock"}, 1)),
FirstArgMutexDescriptor(CallDescription({"mtx_lock"}, 1),
CallDescription({"mtx_unlock"}, 1)),
FirstArgMutexDescriptor(CallDescription({"pthread_mutex_trylock"}, 1),
CallDescription({"pthread_mutex_unlock"}, 1)),
FirstArgMutexDescriptor(CallDescription({"mtx_trylock"}, 1),
CallDescription({"mtx_unlock"}, 1)),
FirstArgMutexDescriptor(CallDescription({"mtx_timedlock"}, 1),
CallDescription({"mtx_unlock"}, 1)),
MemberMutexDescriptor({/*MatchAs=*/CDM::CXXMethod,
/*QualifiedName=*/{"std", "mutex", "lock"},
/*RequiredArgs=*/0},
{CDM::CXXMethod, {"std", "mutex", "unlock"}, 0}),
FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_lock"}, 1},
{CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_lock"}, 1},
{CDM::CLibrary, {"mtx_unlock"}, 1}),
FirstArgMutexDescriptor({CDM::CLibrary, {"pthread_mutex_trylock"}, 1},
{CDM::CLibrary, {"pthread_mutex_unlock"}, 1}),
FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_trylock"}, 1},
{CDM::CLibrary, {"mtx_unlock"}, 1}),
FirstArgMutexDescriptor({CDM::CLibrary, {"mtx_timedlock"}, 1},
{CDM::CLibrary, {"mtx_unlock"}, 1}),
RAIIMutexDescriptor("lock_guard"),
RAIIMutexDescriptor("unique_lock")};

const std::array<CallDescription, 5> BlockingFunctions{
ArrayRef{StringRef{"sleep"}}, ArrayRef{StringRef{"getc"}},
ArrayRef{StringRef{"fgets"}}, ArrayRef{StringRef{"read"}},
ArrayRef{StringRef{"recv"}}};
const CallDescriptionSet BlockingFunctions{{CDM::CLibrary, {"sleep"}},
{CDM::CLibrary, {"getc"}},
{CDM::CLibrary, {"fgets"}},
{CDM::CLibrary, {"read"}},
{CDM::CLibrary, {"recv"}}};

const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
Expand Down Expand Up @@ -291,8 +292,7 @@ void BlockInCriticalSectionChecker::handleUnlock(

bool BlockInCriticalSectionChecker::isBlockingInCritSection(
const CallEvent &Call, CheckerContext &C) const {
return llvm::any_of(BlockingFunctions,
[&Call](auto &&Fn) { return Fn.matches(Call); }) &&
return BlockingFunctions.contains(Call) &&
!C.getState()->get<ActiveCritSections>().isEmpty();
}

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ class CStringChecker : public Checker< eval::Call,
};

// These require a bit of special handling.
CallDescription StdCopy{{"std", "copy"}, 3},
StdCopyBackward{{"std", "copy_backward"}, 3};
CallDescription StdCopy{CDM::SimpleFunc, {"std", "copy"}, 3},
StdCopyBackward{CDM::SimpleFunc, {"std", "copy_backward"}, 3};

FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
void evalMemcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
Expand Down
58 changes: 25 additions & 33 deletions clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,28 @@ namespace {
class InnerPointerChecker
: public Checker<check::DeadSymbols, check::PostCall> {

CallDescription AppendFn, AssignFn, AddressofFn, AddressofFn_, ClearFn,
CStrFn, DataFn, DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn,
ReplaceFn, ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn;
CallDescriptionSet InvalidatingMemberFunctions{
CallDescription(CDM::CXXMethod, {"std", "basic_string", "append"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "assign"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "clear"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "erase"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "insert"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "pop_back"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "push_back"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "replace"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "reserve"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "resize"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "shrink_to_fit"}),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "swap"})};

CallDescriptionSet AddressofFunctions{
CallDescription(CDM::SimpleFunc, {"std", "addressof"}),
CallDescription(CDM::SimpleFunc, {"std", "__addressof"})};

CallDescriptionSet InnerPointerAccessFunctions{
CallDescription(CDM::CXXMethod, {"std", "basic_string", "c_str"}),
CallDescription(CDM::SimpleFunc, {"std", "data"}, 1),
CallDescription(CDM::CXXMethod, {"std", "basic_string", "data"})};

public:
class InnerPointerBRVisitor : public BugReporterVisitor {
Expand Down Expand Up @@ -71,30 +90,10 @@ class InnerPointerChecker
}
};

InnerPointerChecker()
: AppendFn({"std", "basic_string", "append"}),
AssignFn({"std", "basic_string", "assign"}),
AddressofFn({"std", "addressof"}), AddressofFn_({"std", "__addressof"}),
ClearFn({"std", "basic_string", "clear"}),
CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1),
DataMemberFn({"std", "basic_string", "data"}),
EraseFn({"std", "basic_string", "erase"}),
InsertFn({"std", "basic_string", "insert"}),
PopBackFn({"std", "basic_string", "pop_back"}),
PushBackFn({"std", "basic_string", "push_back"}),
ReplaceFn({"std", "basic_string", "replace"}),
ReserveFn({"std", "basic_string", "reserve"}),
ResizeFn({"std", "basic_string", "resize"}),
ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}),
SwapFn({"std", "basic_string", "swap"}) {}

/// Check whether the called member function potentially invalidates
/// pointers referring to the container object's inner buffer.
bool isInvalidatingMemberFunction(const CallEvent &Call) const;

/// Check whether the called function returns a raw inner pointer.
bool isInnerPointerAccessFunction(const CallEvent &Call) const;

/// Mark pointer symbols associated with the given memory region released
/// in the program state.
void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
Expand Down Expand Up @@ -127,14 +126,7 @@ bool InnerPointerChecker::isInvalidatingMemberFunction(
return false;
}
return isa<CXXDestructorCall>(Call) ||
matchesAny(Call, AppendFn, AssignFn, ClearFn, EraseFn, InsertFn,
PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
ShrinkToFitFn, SwapFn);
}

bool InnerPointerChecker::isInnerPointerAccessFunction(
const CallEvent &Call) const {
return matchesAny(Call, CStrFn, DataFn, DataMemberFn);
InvalidatingMemberFunctions.contains(Call);
}

void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
Expand Down Expand Up @@ -181,7 +173,7 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,

// std::addressof functions accepts a non-const reference as an argument,
// but doesn't modify it.
if (matchesAny(Call, AddressofFn, AddressofFn_))
if (AddressofFunctions.contains(Call))
continue;

markPtrSymbolsReleased(Call, State, ArgRegion, C);
Expand Down Expand Up @@ -221,7 +213,7 @@ void InnerPointerChecker::checkPostCall(const CallEvent &Call,
}
}

if (isInnerPointerAccessFunction(Call)) {
if (InnerPointerAccessFunctions.contains(Call)) {

if (isa<SimpleFunctionCall>(Call)) {
// NOTE: As of now, we only have one free access function: std::data.
Expand Down
156 changes: 45 additions & 111 deletions clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
//===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===//
//===- ReturnValueChecker - Check methods always returning true -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This defines ReturnValueChecker, which checks for calls with guaranteed
// boolean return value. It ensures the return value of each function call.
// This defines ReturnValueChecker, which models a very specific coding
// convention within the LLVM/Clang codebase: there several classes that have
// Error() methods which always return true.
// This checker was introduced to eliminate false positives caused by this
// peculiar "always returns true" invariant. (Normally, the analyzer assumes
// that a function returning `bool` can return both `true` and `false`, because
// otherwise it could've been a `void` function.)
//
//===----------------------------------------------------------------------===//

Expand All @@ -18,43 +23,40 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/FormatVariadic.h"
#include <optional>

using namespace clang;
using namespace ento;
using llvm::formatv;

namespace {
class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
class ReturnValueChecker : public Checker<check::PostCall> {
public:
// It sets the predefined invariant ('CDM') if the current call not break it.
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;

// It reports whether a predefined invariant ('CDM') is broken.
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;

private:
// The pairs are in the following form: {{{class, call}}, return value}
const CallDescriptionMap<bool> CDM = {
const CallDescriptionSet Methods = {
// These are known in the LLVM project: 'Error()'
{{{"ARMAsmParser", "Error"}}, true},
{{{"HexagonAsmParser", "Error"}}, true},
{{{"LLLexer", "Error"}}, true},
{{{"LLParser", "Error"}}, true},
{{{"MCAsmParser", "Error"}}, true},
{{{"MCAsmParserExtension", "Error"}}, true},
{{{"TGParser", "Error"}}, true},
{{{"X86AsmParser", "Error"}}, true},
{CDM::CXXMethod, {"ARMAsmParser", "Error"}},
{CDM::CXXMethod, {"HexagonAsmParser", "Error"}},
{CDM::CXXMethod, {"LLLexer", "Error"}},
{CDM::CXXMethod, {"LLParser", "Error"}},
{CDM::CXXMethod, {"MCAsmParser", "Error"}},
{CDM::CXXMethod, {"MCAsmParserExtension", "Error"}},
{CDM::CXXMethod, {"TGParser", "Error"}},
{CDM::CXXMethod, {"X86AsmParser", "Error"}},
// 'TokError()'
{{{"LLParser", "TokError"}}, true},
{{{"MCAsmParser", "TokError"}}, true},
{{{"MCAsmParserExtension", "TokError"}}, true},
{{{"TGParser", "TokError"}}, true},
{CDM::CXXMethod, {"LLParser", "TokError"}},
{CDM::CXXMethod, {"MCAsmParser", "TokError"}},
{CDM::CXXMethod, {"MCAsmParserExtension", "TokError"}},
{CDM::CXXMethod, {"TGParser", "TokError"}},
// 'error()'
{{{"MIParser", "error"}}, true},
{{{"WasmAsmParser", "error"}}, true},
{{{"WebAssemblyAsmParser", "error"}}, true},
{CDM::CXXMethod, {"MIParser", "error"}},
{CDM::CXXMethod, {"WasmAsmParser", "error"}},
{CDM::CXXMethod, {"WebAssemblyAsmParser", "error"}},
// Other
{{{"AsmParser", "printError"}}, true}};
{CDM::CXXMethod, {"AsmParser", "printError"}}};
};
} // namespace

Expand All @@ -68,100 +70,32 @@ static std::string getName(const CallEvent &Call) {
return Name;
}

// The predefinitions ('CDM') could break due to the ever growing code base.
// Check for the expected invariants and see whether they apply.
static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
CheckerContext &C) {
auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
if (!ReturnDV)
return std::nullopt;

if (ExpectedValue)
return C.getState()->isNull(*ReturnDV).isConstrainedTrue();

return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
}

void ReturnValueChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
const bool *RawExpectedValue = CDM.lookup(Call);
if (!RawExpectedValue)
if (!Methods.contains(Call))
return;

SVal ReturnV = Call.getReturnValue();
bool ExpectedValue = *RawExpectedValue;
std::optional<bool> IsInvariantBreak =
isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
auto ReturnV = Call.getReturnValue().getAs<DefinedOrUnknownSVal>();

// If the invariant is broken it is reported by 'checkEndFunction()'.
if (*IsInvariantBreak)
if (!ReturnV)
return;

std::string Name = getName(Call);
const NoteTag *CallTag = C.getNoteTag(
[Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);

Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "true" : "false");
return std::string(Out.str());
},
/*IsPrunable=*/true);

ProgramStateRef State = C.getState();
State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
C.addTransition(State, CallTag);
}

void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &C) const {
if (!RS || !RS->getRetValue())
if (ProgramStateRef StTrue = State->assume(*ReturnV, true)) {
// The return value can be true, so transition to a state where it's true.
std::string Msg =
formatv("'{0}' returns true (by convention)", getName(Call));
C.addTransition(StTrue, C.getNoteTag(Msg, /*IsPrunable=*/true));
return;

// We cannot get the caller in the top-frame.
const StackFrameContext *SFC = C.getStackFrame();
if (C.getStackFrame()->inTopFrame())
return;

ProgramStateRef State = C.getState();
CallEventManager &CMgr = C.getStateManager().getCallEventManager();
CallEventRef<> Call = CMgr.getCaller(SFC, State);
if (!Call)
return;

const bool *RawExpectedValue = CDM.lookup(*Call);
if (!RawExpectedValue)
return;

SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
bool ExpectedValue = *RawExpectedValue;
std::optional<bool> IsInvariantBreak =
isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;

// If the invariant is appropriate it is reported by 'checkPostCall()'.
if (!*IsInvariantBreak)
return;

std::string Name = getName(*Call);
const NoteTag *CallTag = C.getNoteTag(
[Name, ExpectedValue](BugReport &BR) -> std::string {
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);

// The following is swapped because the invariant is broken.
Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "false" : "true");

return std::string(Out.str());
},
/*IsPrunable=*/false);

C.addTransition(State, CallTag);
}
// Paranoia: if the return value is known to be false (which is highly
// unlikely, it's easy to ensure that the method always returns true), then
// produce a note that highlights that this unusual situation.
// Note that this checker is 'hidden' so it cannot produce a bug report.
std::string Msg = formatv("'{0}' returned false, breaking the convention "
"that it always returns true",
getName(Call));
C.addTransition(State, C.getNoteTag(Msg, /*IsPrunable=*/true));
}

void ento::registerReturnValueChecker(CheckerManager &Mgr) {
Expand Down
18 changes: 9 additions & 9 deletions clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ class SmartPtrModeling
using SmartPtrMethodHandlerFn =
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
{{{"reset"}}, &SmartPtrModeling::handleReset},
{{{"release"}}, &SmartPtrModeling::handleRelease},
{{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
{{{"get"}}, &SmartPtrModeling::handleGet}};
const CallDescription StdSwapCall{{"std", "swap"}, 2};
const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
const CallDescription StdMakeUniqueForOverwriteCall{
{"std", "make_unique_for_overwrite"}};
{{CDM::CXXMethod, {"reset"}}, &SmartPtrModeling::handleReset},
{{CDM::CXXMethod, {"release"}}, &SmartPtrModeling::handleRelease},
{{CDM::CXXMethod, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
{{CDM::CXXMethod, {"get"}}, &SmartPtrModeling::handleGet}};
const CallDescription StdSwapCall{CDM::SimpleFunc, {"std", "swap"}, 2};
const CallDescriptionSet MakeUniqueVariants{
{CDM::SimpleFunc, {"std", "make_unique"}},
{CDM::SimpleFunc, {"std", "make_unique_for_overwrite"}}};
};
} // end of anonymous namespace

Expand Down Expand Up @@ -296,7 +296,7 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
}

if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
if (MakeUniqueVariants.contains(Call)) {
if (!ModelSmartPtrDereference)
return false;

Expand Down
8 changes: 5 additions & 3 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,17 +388,19 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
};

CallDescriptionMap<FnDescription> FnTestDescriptions = {
{{{"StreamTesterChecker_make_feof_stream"}, 1},
{{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
false),
0}},
{{{"StreamTesterChecker_make_ferror_stream"}, 1},
{{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError, false),
0}},
{{{"StreamTesterChecker_make_ferror_indeterminate_stream"}, 1},
{{CDM::SimpleFunc,
{"StreamTesterChecker_make_ferror_indeterminate_stream"},
1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError, true),
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
E = tempExpr->getSubExpr();
continue;
}
if (auto *tempExpr = dyn_cast<ParenExpr>(E)) {
E = tempExpr->getSubExpr();
continue;
}
if (auto *cast = dyn_cast<CastExpr>(E)) {
if (StopAtFirstRefCountedObj) {
if (auto *ConversionFunc =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "ASTUtils.h"
#include "DiagOutputUtils.h"
#include "PtrTypesSemantics.h"
#include "clang/AST/CXXInheritance.h"
Expand Down Expand Up @@ -90,6 +91,9 @@ class RefCntblBaseVirtualDtorChecker
const CXXRecordDecl *C = T->getAsCXXRecordDecl();
if (!C)
return false;
if (isRefCountedClass(C))
return false;

bool AnyInconclusiveBase = false;
const auto hasPublicRefInBase =
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
Expand Down Expand Up @@ -164,6 +168,20 @@ class RefCntblBaseVirtualDtorChecker
return false;
}

static bool isRefCountedClass(const CXXRecordDecl *D) {
if (!D->getTemplateInstantiationPattern())
return false;
auto *NsDecl = D->getParent();
if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
return false;
auto NamespaceName = safeGetName(NsDecl);
auto ClsNameStr = safeGetName(D);
StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
return NamespaceName == "WTF" &&
(ClsName.ends_with("RefCounted") ||
ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
}

void reportBug(const CXXRecordDecl *DerivedClass,
const CXXBaseSpecifier *BaseSpec,
const CXXRecordDecl *ProblematicBaseClass) const {
Expand Down
117 changes: 117 additions & 0 deletions clang/test/AST/Interp/eval-order.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu
// RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu -fexperimental-new-constant-interpreter

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

/// Check that assignment operators evaluate their operands right-to-left.
/// Copied from test/SemaCXX/constant-expression-cxx1z.cpp
///
/// As you can see from the FIXME comments, some of these are not yet working correctly
/// in the new interpreter.
namespace EvalOrder {
template<typename T> struct lvalue {
T t;
constexpr T &get() { return t; }
};

struct UserDefined {
int n = 0;
constexpr UserDefined &operator=(const UserDefined&) { return *this; }
constexpr UserDefined &operator+=(const UserDefined&) { return *this; }
constexpr void operator<<(const UserDefined&) const {}
constexpr void operator>>(const UserDefined&) const {}
constexpr void operator+(const UserDefined&) const {}
constexpr void operator[](int) const {}
};
constexpr UserDefined ud;

struct NonMember {};
constexpr void operator+=(NonMember, NonMember) {}
constexpr void operator<<(NonMember, NonMember) {}
constexpr void operator>>(NonMember, NonMember) {}
constexpr void operator+(NonMember, NonMember) {}
constexpr NonMember nm;

constexpr void f(...) {}

// Helper to ensure that 'a' is evaluated before 'b'.
struct seq_checker {
bool done_a = false;
bool done_b = false;

template <typename T> constexpr T &&a(T &&v) {
done_a = true;
return (T &&)v;
}
template <typename T> constexpr T &&b(T &&v) {
if (!done_a)
throw "wrong";
done_b = true;
return (T &&)v;
}

constexpr bool ok() { return done_a && done_b; }
};

// SEQ(expr), where part of the expression is tagged A(...) and part is
// tagged B(...), checks that A is evaluated before B.
#define A sc.a
#define B sc.b
#define SEQ(...) static_assert([](seq_checker sc) { void(__VA_ARGS__); return sc.ok(); }({}))

// Longstanding sequencing rules.
SEQ((A(1), B(2)));
SEQ((A(true) ? B(2) : throw "huh?"));
SEQ((A(false) ? throw "huh?" : B(2)));
SEQ(A(true) && B(true));
SEQ(A(false) || B(true));

// From P0145R3:

// Rules 1 and 2 have no effect ('b' is not an expression).

// Rule 3: a->*b
// SEQ(A(ud).*B(&UserDefined::n)); FIXME
// SEQ(A(&ud)->*B(&UserDefined::n)); FIXME

// Rule 4: a(b1, b2, b3)
// SEQ(A(f)(B(1), B(2), B(3))); FIXME

// Rule 5: b = a, b @= a
// SEQ(B(lvalue<int>().get()) = A(0)); FIXME
// SEQ(B(lvalue<UserDefined>().get()) = A(ud)); FIXME
SEQ(B(lvalue<int>().get()) += A(0));
// SEQ(B(lvalue<UserDefined>().get()) += A(ud)); FIXME
// SEQ(B(lvalue<NonMember>().get()) += A(nm)); FIXME

// Rule 6: a[b]
constexpr int arr[3] = {};
SEQ(A(arr)[B(0)]);
SEQ(A(+arr)[B(0)]);
// SEQ(A(0)[B(arr)]); FIXME
// SEQ(A(0)[B(+arr)]); FIXME
SEQ(A(ud)[B(0)]);

// Rule 7: a << b
SEQ(A(1) << B(2));
SEQ(A(ud) << B(ud));
SEQ(A(nm) << B(nm));

// Rule 8: a >> b
SEQ(A(1) >> B(2));
SEQ(A(ud) >> B(ud));
SEQ(A(nm) >> B(nm));

// No particular order of evaluation is specified in other cases, but we in
// practice evaluate left-to-right.
// FIXME: Technically we're expected to check for undefined behavior due to
// unsequenced read and modification and treat it as non-constant due to UB.
SEQ(A(1) + B(2));
SEQ(A(ud) + B(ud));
SEQ(A(nm) + B(nm));
SEQ(f(A(1), B(2)));
#undef SEQ
#undef A
#undef B
}
5 changes: 5 additions & 0 deletions clang/test/AST/ast-dump-recovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ void InitializerOfInvalidDecl() {
// CHECK: VarDecl {{.*}} invalid InvalidDecl
// CHECK-NEXT: `-RecoveryExpr {{.*}} '<dependent type>' contains-errors
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'ValidDecl'

Unknown InvalidDeclWithInvalidInit = Invalid;
// CHECK: VarDecl {{.*}} invalid InvalidDeclWithInvalidInit
// CHECK-NEXT: `-RecoveryExpr {{.*}} '<dependent type>' contains-errors
// CHECK-NOT: `-TypoExpr
}

void RecoverToAnInvalidDecl() {
Expand Down
11 changes: 11 additions & 0 deletions clang/test/Analysis/Checkers/WebKit/call-args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,17 @@ namespace default_arg {
}
}

namespace cxx_member_func {
Ref<RefCountable> provideProtected();
void foo() {
provide()->trivial();
provide()->method();
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
provideProtected()->method();
(provideProtected())->method();
};
}

namespace cxx_member_operator_call {
// The hidden this-pointer argument without a corresponding parameter caused couple bugs in parameter <-> argument attribution.
struct Foo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,63 @@ struct DerivedClassTmpl3 : T { };

typedef DerivedClassTmpl3<RefCntblBase> Foo;
Foo c;


namespace WTF {

class RefCountedBase {
public:
void ref() const { ++count; }

protected:
bool derefBase() const
{
return !--count;
}

private:
mutable unsigned count;
};

template <typename T>
class RefCounted : public RefCountedBase {
public:
void deref() const {
if (derefBase())
delete const_cast<T*>(static_cast<const T*>(this));
}

protected:
RefCounted() { }
};

template <typename T>
class ThreadSafeRefCounted {
public:
void ref() const;
bool deref() const;
};

template <typename T>
class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
public:
void ref() const;
bool deref() const;
};

} // namespace WTF

class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { };

class DerivedClass5 : public DerivedClass4 { };
// expected-warning@-1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}

class DerivedClass6 : public WTF::ThreadSafeRefCounted<DerivedClass6> { };

class DerivedClass7 : public DerivedClass6 { };
// expected-warning@-1{{Class 'DerivedClass6' is used as a base of class 'DerivedClass7' but doesn't have virtual destructor}}

class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8> { };

class DerivedClass9 : public DerivedClass8 { };
// expected-warning@-1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}}
66 changes: 33 additions & 33 deletions clang/test/Analysis/return-value-guaranteed.cpp
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,apiModeling.llvm.ReturnValue \
// RUN: -analyzer-output=text -verify=class %s
// RUN: -analyzer-output=text -verify %s

struct Foo { int Field; };
bool problem();
void doSomething();

// We predefined the return value of 'MCAsmParser::Error' as true and we cannot
// take the false-branches which leads to a "garbage value" false positive.
namespace test_classes {
// Test the normal case when the implementation of MCAsmParser::Error() (one of
// the methods modeled by this checker) is opaque.
namespace test_normal {
struct MCAsmParser {
static bool Error();
};

bool parseFoo(Foo &F) {
if (problem()) {
// class-note@-1 {{Assuming the condition is false}}
// class-note@-2 {{Taking false branch}}
// expected-note@-1 {{Assuming the condition is false}}
// expected-note@-2 {{Taking false branch}}
return MCAsmParser::Error();
}

F.Field = 0;
// class-note@-1 {{The value 0 is assigned to 'F.Field'}}
return !MCAsmParser::Error();
// class-note@-1 {{'MCAsmParser::Error' returns true}}
// expected-note@-1 {{The value 0 is assigned to 'F.Field'}}
return false;
}

bool parseFile() {
Foo F;
if (parseFoo(F)) {
// class-note@-1 {{Calling 'parseFoo'}}
// class-note@-2 {{Returning from 'parseFoo'}}
// class-note@-3 {{Taking false branch}}
// expected-note@-1 {{Calling 'parseFoo'}}
// expected-note@-2 {{Returning from 'parseFoo'}}
// expected-note@-3 {{Taking false branch}}
return true;
}

// The following expression would produce the false positive report
// "The left operand of '==' is a garbage value"
// without the modeling done by apiModeling.llvm.ReturnValue:
if (F.Field == 0) {
// class-note@-1 {{Field 'Field' is equal to 0}}
// class-note@-2 {{Taking true branch}}

// no-warning: "The left operand of '==' is a garbage value" was here.
// expected-note@-1 {{Field 'Field' is equal to 0}}
// expected-note@-2 {{Taking true branch}}
doSomething();
}

// Trigger a zero division to get path notes:
(void)(1 / F.Field);
// class-warning@-1 {{Division by zero}}
// class-note@-2 {{Division by zero}}
// expected-warning@-1 {{Division by zero}}
// expected-note@-2 {{Division by zero}}
return false;
}
} // namespace test_classes
} // namespace test_normal


// We predefined 'MCAsmParser::Error' as returning true, but now it returns
// false, which breaks our invariant. Test the notes.
// Sanity check for the highly unlikely case where the implementation of the
// method breaks the convention.
namespace test_break {
struct MCAsmParser {
static bool Error() {
return false; // class-note {{'MCAsmParser::Error' returns false}}
return false;
}
};

bool parseFoo(Foo &F) {
if (problem()) {
// class-note@-1 {{Assuming the condition is false}}
// class-note@-2 {{Taking false branch}}
// expected-note@-1 {{Assuming the condition is false}}
// expected-note@-2 {{Taking false branch}}
return !MCAsmParser::Error();
}

F.Field = 0;
// class-note@-1 {{The value 0 is assigned to 'F.Field'}}
// expected-note@-1 {{The value 0 is assigned to 'F.Field'}}
return MCAsmParser::Error();
// class-note@-1 {{Calling 'MCAsmParser::Error'}}
// class-note@-2 {{Returning from 'MCAsmParser::Error'}}
// expected-note@-1 {{'MCAsmParser::Error' returned false, breaking the convention that it always returns true}}
}

bool parseFile() {
Foo F;
if (parseFoo(F)) {
// class-note@-1 {{Calling 'parseFoo'}}
// class-note@-2 {{Returning from 'parseFoo'}}
// class-note@-3 {{Taking false branch}}
// expected-note@-1 {{Calling 'parseFoo'}}
// expected-note@-2 {{Returning from 'parseFoo'}}
// expected-note@-3 {{Taking false branch}}
return true;
}

(void)(1 / F.Field);
// class-warning@-1 {{Division by zero}}
// class-note@-2 {{Division by zero}}
// expected-warning@-1 {{Division by zero}}
// expected-note@-2 {{Division by zero}}
return false;
}
} // namespace test_classes
} // namespace test_break
24 changes: 24 additions & 0 deletions clang/test/CodeGen/aarch64-soft-float-abi-errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ inline void test_float_arg_inline(float a) {}
inline void test_float_arg_inline_used(float a) {}
// nofp-hard-opt-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}}
void use_inline() { test_float_arg_inline_used(1.0f); }
// nofp-hard-error@-1 {{'use_inline' requires 'float' type support, but ABI 'aapcs' does not support it}}

// The always_inline attribute causes an inline function to always be
// code-genned, even at -O0, so we always emit the error.
__attribute((always_inline))
inline void test_float_arg_always_inline_used(float a) {}
// nofp-hard-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}}
void use_always_inline() { test_float_arg_always_inline_used(1.0f); }
// nofp-hard-error@-1 {{'use_always_inline' requires 'float' type support, but ABI 'aapcs' does not support it}}

// Floating-point expressions, global variables and local variables do not
// affect the ABI, so are allowed. GCC does reject some uses of floating point
Expand All @@ -97,3 +99,25 @@ int test_var_double(int a) {
d *= 6.0;
return (int)d;
}

extern void extern_float_arg(float);
extern float extern_float_ret(void);
void call_extern_float_arg() { extern_float_arg(1.0f); }
// nofp-hard-error@-1 {{'call_extern_float_arg' requires 'float' type support, but ABI 'aapcs' does not support it}}
void call_extern_float_ret() { extern_float_ret(); }
// nofp-hard-error@-1 {{'call_extern_float_ret' requires 'float' type support, but ABI 'aapcs' does not support it}}

// Definitions of variadic functions, and calls to them which only use integer
// argument registers, are both fine.
void variadic(int, ...);
void call_variadic_int() { variadic(0, 1); }

// Calls to variadic functions with floating-point arguments are an error,
// since this would require floating-point registers.
void call_variadic_double() { variadic(0, 1.0); }
// nofp-hard-error@-1 {{'call_variadic_double' requires 'double' type support, but ABI 'aapcs' does not support it}}

// Calls through function pointers are also diagnosed.
void (*fptr)(float);
void call_indirect() { fptr(1.0f); }
// nofp-hard-error@-1 {{'call_indirect' requires 'float' type support, but ABI 'aapcs' does not support it}}
8 changes: 4 additions & 4 deletions clang/test/Driver/arm-cortex-cpus-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,23 @@
// RUN: %clang -target armv8r-linux-gnueabi -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R %s
// RUN: %clang -target arm -march=armv8r -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R %s
// RUN: %clang -target arm -march=armv8-r -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R %s
// CHECK-V8R: "-cc1"{{.*}} "-triple" "armv8r-{{.*}} "-target-cpu" "cortex-r52"
// CHECK-V8R: "-cc1"{{.*}} "-triple" "armv8r-{{.*}} "-target-cpu" "generic"

// RUN: %clang -target armv8r-linux-gnueabi -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R-BIG %s
// RUN: %clang -target arm -march=armv8r -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R-BIG %s
// RUN: %clang -target arm -march=armv8-r -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8R-BIG %s
// CHECK-V8R-BIG: "-cc1"{{.*}} "-triple" "armebv8r-{{.*}} "-target-cpu" "cortex-r52"
// CHECK-V8R-BIG: "-cc1"{{.*}} "-triple" "armebv8r-{{.*}} "-target-cpu" "generic"

// RUN: %clang -target armv8r-linux-gnueabi -mthumb -### -c %s 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-V8R-THUMB %s
// RUN: %clang -target arm -march=armv8r -mthumb -### -c %s 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-V8R-THUMB %s
// CHECK-V8R-THUMB: "-cc1"{{.*}} "-triple" "thumbv8r-{{.*}} "-target-cpu" "cortex-r52"
// CHECK-V8R-THUMB: "-cc1"{{.*}} "-triple" "thumbv8r-{{.*}} "-target-cpu" "generic"
// RUN: %clang -target armv8r-linux-gnueabi -mthumb -mbig-endian -### -c %s 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-V8R-THUMB-BIG %s
// RUN: %clang -target arm -march=armv8r -mthumb -mbig-endian -### -c %s 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-V8R-THUMB-BIG %s
// CHECK-V8R-THUMB-BIG: "-cc1"{{.*}} "-triple" "thumbebv8r-{{.*}} "-target-cpu" "cortex-r52"
// CHECK-V8R-THUMB-BIG: "-cc1"{{.*}} "-triple" "thumbebv8r-{{.*}} "-target-cpu" "generic"

// RUN: %clang -mcpu=generic -target armv8 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8A-GENERIC %s
// RUN: %clang -mcpu=generic -target arm -march=armv8 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V8A-GENERIC %s
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Driver/arm-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
// Check +crypto for M and R profiles:
//
// RUN: %clang -target arm-arm-none-eabi -march=armv8-r+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-CRYPTO-R %s
// CHECK-CRYPTO-R: "-cc1"{{.*}} "-target-cpu" "cortex-r52"{{.*}} "-target-feature" "+sha2" "-target-feature" "+aes"
// CHECK-CRYPTO-R: "-cc1"{{.*}} "-target-cpu" "generic"{{.*}} "-target-feature" "+sha2" "-target-feature" "+aes"
// RUN: %clang -target arm-arm-none-eabi -march=armv8-m.base+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-NOCRYPTO5 %s
// RUN: %clang -target arm-arm-none-eabi -march=armv8-m.main+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-NOCRYPTO5 %s
// RUN: %clang -target arm-arm-none-eabi -mcpu=cortex-m23+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-NOCRYPTO5 %s
Expand Down
1 change: 1 addition & 0 deletions clang/test/Headers/arm-acle-header.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// RUN: %clang_cc1 -x c++ -triple thumbv7-windows -target-cpu cortex-a15 -fsyntax-only -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=19.11 %s
// RUN: %clang_cc1 -x c++ -triple aarch64-windows -target-cpu cortex-a53 -fsyntax-only -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=19.11 %s
// RUN: %clang_cc1 -x c++ -triple arm64-apple-ios -target-cpu apple-a7 -fsyntax-only -ffreestanding -fms-extensions %s
// RUN: %clang_cc1 -x c++ -triple arm64ec-windows -target-cpu cortex-a53 -fsyntax-only -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=19.11 %s
// expected-no-diagnostics

#include <arm_acle.h>
Expand Down
94 changes: 94 additions & 0 deletions clang/test/Modules/function-transitive-change.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Test that, in C++20 modules reduced BMI, the implementation detail changes
// in non-inline function may not propagate while the inline function changes
// can get propagate.
//
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: cd %t
//
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm
// RUN: %clang_cc1 -std=c++20 %t/a.v1.cppm -emit-reduced-module-interface -o %t/a.v1.pcm
//
// The BMI of A should differ since the different implementation.
// RUN: not diff %t/a.pcm %t/a.v1.pcm &> /dev/null
//
// The BMI of B should change since the dependent inline function changes
// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-reduced-module-interface -fmodule-file=a=%t/a.pcm \
// RUN: -o %t/b.pcm
// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-reduced-module-interface -fmodule-file=a=%t/a.v1.pcm \
// RUN: -o %t/b.v1.pcm
// RUN: not diff %t/b.v1.pcm %t/b.pcm &> /dev/null
//
// Test the case with unused partitions.
// RUN: %clang_cc1 -std=c++20 %t/M-A.cppm -emit-reduced-module-interface -o %t/M-A.pcm
// RUN: %clang_cc1 -std=c++20 %t/M-B.cppm -emit-reduced-module-interface -o %t/M-B.pcm
// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-reduced-module-interface -o %t/M.pcm \
// RUN: -fmodule-file=M:partA=%t/M-A.pcm \
// RUN: -fmodule-file=M:partB=%t/M-B.pcm
// RUN: %clang_cc1 -std=c++20 %t/N.cppm -emit-reduced-module-interface -o %t/N.pcm \
// RUN: -fmodule-file=M:partA=%t/M-A.pcm \
// RUN: -fmodule-file=M:partB=%t/M-B.pcm \
// RUN: -fmodule-file=M=%t/M.pcm
//
// Now we change `M-A.cppm` to `M-A.v1.cppm`.
// RUN: %clang_cc1 -std=c++20 %t/M-A.v1.cppm -emit-reduced-module-interface -o %t/M-A.v1.pcm
// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-reduced-module-interface -o %t/M.v1.pcm \
// RUN: -fmodule-file=M:partA=%t/M-A.v1.pcm \
// RUN: -fmodule-file=M:partB=%t/M-B.pcm
// RUN: %clang_cc1 -std=c++20 %t/N.cppm -emit-reduced-module-interface -o %t/N.v1.pcm \
// RUN: -fmodule-file=M:partA=%t/M-A.v1.pcm \
// RUN: -fmodule-file=M:partB=%t/M-B.pcm \
// RUN: -fmodule-file=M=%t/M.v1.pcm
//
// The BMI of N can keep unchanged since the N didn't use the changed partition unit 'M:A'.
// RUN: diff %t/N.v1.pcm %t/N.pcm &> /dev/null

//--- a.cppm
export module a;
export inline int a() {
return 48;
}

//--- a.v1.cppm
export module a;
export inline int a() {
return 50;
}

//--- b.cppm
export module b;
import a;
export inline int b() {
return a();
}

//--- M-A.cppm
export module M:partA;
export inline int a() {
return 43;
}

//--- M-A.v1.cppm
export module M:partA;
export inline int a() {
return 50;
}

//--- M-B.cppm
export module M:partB;
export inline int b() {
return 44;
}

//--- M.cppm
export module M;
export import :partA;
export import :partB;

//--- N.cppm
export module N;
import M;

export inline int n() {
return b();
}
21 changes: 0 additions & 21 deletions clang/test/Modules/no-transitive-source-location-change.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,6 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
// RUN: %clang_cc1 -std=c++20 %t/A.v1.cppm -emit-module-interface -o %t/A.v1.pcm
//
// The BMI may not be the same since the source location differs.
// RUN: not diff %t/A.pcm %t/A.v1.pcm &> /dev/null
//
// The BMI of B shouldn't change since all the locations remain the same.
// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -fmodule-file=A=%t/A.pcm \
// RUN: -o %t/B.pcm
// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -fmodule-file=A=%t/A.v1.pcm \
// RUN: -o %t/B.v1.pcm
// RUN: diff %t/B.v1.pcm %t/B.pcm &> /dev/null
//
// The BMI of C may change since the locations for instantiations changes.
// RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-module-interface -fmodule-file=A=%t/A.pcm \
// RUN: -o %t/C.pcm
// RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-module-interface -fmodule-file=A=%t/A.v1.pcm \
// RUN: -o %t/C.v1.pcm
// RUN: not diff %t/C.v1.pcm %t/C.pcm &> /dev/null
//
// Test again with reduced BMI.
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-reduced-module-interface -o %t/A.pcm
// RUN: %clang_cc1 -std=c++20 %t/A.v1.cppm -emit-reduced-module-interface -o %t/A.v1.pcm
//
Expand Down
47 changes: 47 additions & 0 deletions clang/test/Modules/pr91105.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 %t/bar.cppm -emit-module-interface -o %t/bar.pcm
// RUN: %clang_cc1 -std=c++20 %t/foo.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify
//
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/bar.cppm -emit-module-interface \
// RUN: -o %t/bar.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/foo.cc \
// RUN: -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify
//
// RUN: %clang_cc1 -std=c++20 %t/bar.cppm -emit-reduced-module-interface -o %t/bar.pcm
// RUN: %clang_cc1 -std=c++20 %t/foo.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify
//
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/bar.cppm -emit-reduced-module-interface \
// RUN: -o %t/bar.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/foo.cc \
// RUN: -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify

//--- h.hpp
#pragma once

struct T {
constexpr T(const char *) {}
};
template <char... c>
struct t {
inline constexpr operator T() const { return {s}; }

private:
inline static constexpr char s[]{c..., '\0'};
};

//--- bar.cppm
module;
#include "h.hpp"
export module bar;
export inline constexpr auto k = t<'k'>{};

//--- foo.cc
// expected-no-diagnostics
#include "h.hpp"
import bar;
void f() {
T x = k;
}
4 changes: 2 additions & 2 deletions clang/test/Preprocessor/arm-target-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
// CHECK-V8R: #define __ARM_FEATURE_NUMERIC_MAXMIN 1
// CHECK-V8R-NOT: #define __ARM_FP 0x

// RUN: %clang -target armv8r-none-linux-gnueabi -x c -E -dM %s -o - | FileCheck -match-full-lines --check-prefix=CHECK-V8R-ALLOW-FP-INSTR %s
// RUN: %clang -target armv8r-none-linux-gnueabihf -x c -E -dM %s -o - | FileCheck -match-full-lines --check-prefix=CHECK-V8R-ALLOW-FP-INSTR %s
// RUN: %clang -target armv8r-none-linux-gnueabi -mcpu=cortex-r52 -x c -E -dM %s -o - | FileCheck -match-full-lines --check-prefix=CHECK-V8R-ALLOW-FP-INSTR %s
// RUN: %clang -target armv8r-none-linux-gnueabihf -mcpu=cortex-r52 -x c -E -dM %s -o - | FileCheck -match-full-lines --check-prefix=CHECK-V8R-ALLOW-FP-INSTR %s
// CHECK-V8R-ALLOW-FP-INSTR: #define __ARMEL__ 1
// CHECK-V8R-ALLOW-FP-INSTR: #define __ARM_ARCH 8
// CHECK-V8R-ALLOW-FP-INSTR: #define __ARM_ARCH_8R__ 1
Expand Down
14 changes: 14 additions & 0 deletions clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,17 @@ using AFoo = Foo<int, Derived<U>>;

AFoo a(Derived<int>{});
} // namespace test22

namespace test23 {
// We have an aggregate deduction guide "G(T) -> G<T>".
template<typename T>
struct G { T t1; };

template<typename X = int>
using AG = G<int>;

AG ag(1.0);
// Verify that the aggregate deduction guide "AG(int) -> AG<int>" is built and
// choosen.
static_assert(__is_same(decltype(ag.t1), int));
} // namespace test23
7 changes: 7 additions & 0 deletions clang/test/SemaTemplate/deduction-guide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,13 @@ AG ag = {1};
// CHECK: | `-BuiltinType {{.*}} 'int'
// CHECK: `-ParmVarDecl {{.*}} 'int'

template <typename X = int>
using BG = G<int>;
BG bg(1.0);
// CHECK-LABEL: Dumping <deduction guide for BG>
// CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for BG>
// CHECK: |-CXXDeductionGuideDecl {{.*}} 'auto (int) -> G<int>' aggregate

template <typename D>
requires (sizeof(D) == 4)
struct Foo {
Expand Down
88 changes: 88 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4709,6 +4709,94 @@ TEST(TransferTest, BooleanInequality) {
});
}

TEST(TransferTest, PointerEquality) {
std::string Code = R"cc(
void target() {
int i = 0;
int i_other = 0;
int *p1 = &i;
int *p2 = &i;
int *p_other = &i_other;
int *null = nullptr;

bool p1_eq_p1 = (p1 == p1);
bool p1_eq_p2 = (p1 == p2);
bool p1_eq_p_other = (p1 == p_other);

bool p1_eq_null = (p1 == null);
bool p1_eq_nullptr = (p1 == nullptr);
bool null_eq_nullptr = (null == nullptr);
bool nullptr_eq_nullptr = (nullptr == nullptr);

// We won't duplicate all of the tests above with `!=`, as we know that
// the implementation simply negates the result of the `==` comparison.
// Instaed, just spot-check one case.
bool p1_ne_p1 = (p1 != p1);

(void)0; // [[p]]
}
)cc";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

// Check the we have indeed set things up so that `p1` and `p2` have
// different pointer values.
EXPECT_NE(&getValueForDecl<PointerValue>(ASTCtx, Env, "p1"),
&getValueForDecl<PointerValue>(ASTCtx, Env, "p2"));

EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p1"),
&Env.getBoolLiteralValue(true));
EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p2"),
&Env.getBoolLiteralValue(true));
EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p_other")));

EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_null")));
EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_nullptr")));
EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "null_eq_nullptr"),
&Env.getBoolLiteralValue(true));
EXPECT_EQ(
&getValueForDecl<BoolValue>(ASTCtx, Env, "nullptr_eq_nullptr"),
&Env.getBoolLiteralValue(true));

EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_ne_p1"),
&Env.getBoolLiteralValue(false));
});
}

TEST(TransferTest, PointerEqualityUnionMembers) {
std::string Code = R"cc(
union U {
int i1;
int i2;
};
void target() {
U u;
bool i1_eq_i2 = (&u.i1 == &u.i2);

(void)0; // [[p]]
}
)cc";
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");

// FIXME: By the standard, `u.i1` and `u.i2` should have the same
// address, but we don't yet model this property of union members
// correctly. The result is therefore weaker than it could be (just an
// atom rather than a true literal), though not wrong.
EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "i1_eq_i2")));
});
}

TEST(TransferTest, IntegerLiteralEquality) {
std::string Code = R"(
void target() {
Expand Down
2 changes: 2 additions & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
Style.Language = FormatStyle::LK_Cpp;
CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine);
CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);
CHECK_PARSE_BOOL(AllowShortCaseExpressionOnASingleLine);
CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);
CHECK_PARSE_BOOL(AllowShortCompoundRequirementOnASingleLine);
CHECK_PARSE_BOOL(AllowShortEnumsOnASingleLine);
Expand Down Expand Up @@ -205,6 +206,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements,
AcrossEmptyLines);
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AcrossComments);
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AlignCaseArrows);
CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AlignCaseColons);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass);
Expand Down
171 changes: 171 additions & 0 deletions clang/unittests/Format/FormatTestJava.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,177 @@ TEST_F(FormatTestJava, ConfigurableSpacesInSquareBrackets) {
verifyFormat("types[ i ] = arguments[ i ].getClass();", Spaces);
}

TEST_F(FormatTestJava, SwitchExpression) {
auto Style = getLLVMStyle(FormatStyle::LK_Java);
EXPECT_TRUE(Style.AllowShortCaseExpressionOnASingleLine);

verifyFormat("foo(switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 1;\n"
"});",
Style);

constexpr StringRef Code1{"i = switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 0;\n"
"};"};
verifyFormat(Code1, Style);

Style.IndentCaseLabels = true;
verifyFormat(Code1, Style);

constexpr StringRef Code2{"i = switch (day) {\n"
" case THURSDAY, SATURDAY -> {\n"
" foo();\n"
" yield 8;\n"
" }\n"
" case WEDNESDAY -> {\n"
" bar();\n"
" yield 9;\n"
" }\n"
" default -> {\n"
" yield 0;\n"
" }\n"
"};"};
verifyFormat(Code2, Style);

Style.IndentCaseLabels = false;
verifyFormat(Code2, Style);

constexpr StringRef Code3{"switch (day) {\n"
"case THURSDAY, SATURDAY -> i = 8;\n"
"case WEDNESDAY -> i = 9;\n"
"default -> i = 0;\n"
"};"};
verifyFormat(Code3, Style);

Style.IndentCaseLabels = true;
verifyFormat("switch (day) {\n"
" case THURSDAY, SATURDAY -> i = 8;\n"
" case WEDNESDAY -> i = 9;\n"
" default -> i = 0;\n"
"};",
Code3, Style);
}

TEST_F(FormatTestJava, ShortCaseExpression) {
auto Style = getLLVMStyle(FormatStyle::LK_Java);

verifyFormat("i = switch (a) {\n"
" case 1 -> 1;\n"
" case 2 -> // comment\n"
" 2;\n"
" case 3 ->\n"
" // comment\n"
" 3;\n"
" case 4 -> 4; // comment\n"
" default -> 0;\n"
"};",
Style);

verifyNoChange("i = switch (a) {\n"
" case 1 -> 1;\n"
" // comment\n"
" case 2 -> 2;\n"
" // comment 1\n"
" // comment 2\n"
" case 3 -> 3; /* comment */\n"
" case 4 -> /* comment */ 4;\n"
" case 5 -> x + /* comment */ 1;\n"
" default ->\n"
" 0; // comment line 1\n"
" // comment line 2\n"
"};",
Style);

Style.ColumnLimit = 18;
verifyFormat("i = switch (a) {\n"
" case Monday ->\n"
" 1;\n"
" default -> 9999;\n"
"};",
Style);

Style.ColumnLimit = 80;
Style.AllowShortCaseExpressionOnASingleLine = false;
Style.IndentCaseLabels = true;
verifyFormat("i = switch (n) {\n"
" default /*comments*/ ->\n"
" 1;\n"
" case 0 ->\n"
" 0;\n"
"};",
Style);

Style.AllowShortCaseExpressionOnASingleLine = true;
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
Style.BraceWrapping.AfterCaseLabel = true;
Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always;
verifyFormat("i = switch (n)\n"
"{\n"
" case 0 ->\n"
" {\n"
" yield 0;\n"
" }\n"
" default ->\n"
" {\n"
" yield 1;\n"
" }\n"
"};",
Style);
}

TEST_F(FormatTestJava, AlignCaseArrows) {
auto Style = getLLVMStyle(FormatStyle::LK_Java);
Style.AlignConsecutiveShortCaseStatements.Enabled = true;

verifyFormat("foo(switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 1;\n"
"});",
Style);

verifyFormat("i = switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 0;\n"
"};",
Style);

verifyFormat("switch (day) {\n"
"case THURSDAY, SATURDAY -> i = 8;\n"
"case WEDNESDAY -> i = 9;\n"
"default -> i = 0;\n"
"};",
Style);

Style.AlignConsecutiveShortCaseStatements.AlignCaseArrows = true;

verifyFormat("foo(switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 1;\n"
"});",
Style);

verifyFormat("i = switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 0;\n"
"};",
Style);

verifyFormat("switch (day) {\n"
"case THURSDAY, SATURDAY -> i = 8;\n"
"case WEDNESDAY -> i = 9;\n"
"default -> i = 0;\n"
"};",
Style);
}

} // namespace
} // namespace test
} // namespace format
Expand Down
18 changes: 18 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2981,6 +2981,24 @@ TEST_F(TokenAnnotatorTest, BlockLBrace) {
EXPECT_BRACE_KIND(Tokens[5], BK_Block);
}

TEST_F(TokenAnnotatorTest, SwitchExpression) {
auto Style = getLLVMStyle(FormatStyle::LK_Java);
auto Tokens = annotate("i = switch (day) {\n"
" case THURSDAY, SATURDAY -> 8;\n"
" case WEDNESDAY -> 9;\n"
" default -> 1;\n"
"};",
Style);
ASSERT_EQ(Tokens.size(), 26u) << Tokens;
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_SwitchExpressionLBrace);
EXPECT_TOKEN(Tokens[7], tok::kw_case, TT_SwitchExpressionLabel);
EXPECT_TOKEN(Tokens[11], tok::arrow, TT_CaseLabelArrow);
EXPECT_TOKEN(Tokens[14], tok::kw_case, TT_SwitchExpressionLabel);
EXPECT_TOKEN(Tokens[16], tok::arrow, TT_CaseLabelArrow);
EXPECT_TOKEN(Tokens[19], tok::kw_default, TT_SwitchExpressionLabel);
EXPECT_TOKEN(Tokens[20], tok::arrow, TT_CaseLabelArrow);
}

} // namespace
} // namespace format
} // namespace clang
2 changes: 2 additions & 0 deletions flang/include/flang/Semantics/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ inline bool HasCUDAAttr(const Symbol &sym) {

inline bool NeedCUDAAlloc(const Symbol &sym) {
bool inDeviceSubprogram{IsCUDADeviceContext(&sym.owner())};
if (Fortran::semantics::IsDummy(sym))
return false;
if (const auto *details{
sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) {
if (details->cudaDataAttr() &&
Expand Down
1 change: 0 additions & 1 deletion flang/lib/Lower/OpenMP/ClauseProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,6 @@ bool ClauseProcessor::processCopyin() const {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());

auto checkAndCopyHostAssociateVar =
[&](Fortran::semantics::Symbol *sym,
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) {
Expand Down
2 changes: 1 addition & 1 deletion flang/lib/Lower/OpenMP/Decomposer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ ConstructQueue buildConstructQueue(
List<UnitConstruct> constructs;

ConstructDecomposition decompose(modOp, semaCtx, eval, compound, clauses);
assert(!decompose.output.empty());
assert(!decompose.output.empty() && "Construct decomposition failed");

llvm::SmallVector<llvm::omp::Directive> loweringUnits;
std::ignore =
Expand Down
5 changes: 5 additions & 0 deletions flang/lib/Lower/OpenMP/Decomposer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ using ConstructQueue = List<UnitConstruct>;
LLVM_DUMP_METHOD llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
const UnitConstruct &uc);

// Given a potentially compound construct with a list of clauses that
// apply to it, break it up into individual sub-constructs each with
// the subset of applicable clauses (plus implicit clauses, if any).
// From that create a work queue where each work item corresponds to
// the sub-construct with its clauses.
ConstructQueue buildConstructQueue(mlir::ModuleOp modOp,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
Expand Down
19 changes: 12 additions & 7 deletions flang/lib/Semantics/check-omp-structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,10 @@ class OmpCycleChecker {

bool Pre(const parser::DoConstruct &dc) {
cycleLevel_--;
const auto &labelName{std::get<0>(std::get<0>(dc.t).statement.t)};
if (labelName) {
labelNamesandLevels_.emplace(labelName.value().ToString(), cycleLevel_);
const auto &constructName{std::get<0>(std::get<0>(dc.t).statement.t)};
if (constructName) {
constructNamesAndLevels_.emplace(
constructName.value().ToString(), cycleLevel_);
}
return true;
}
Expand All @@ -105,10 +106,14 @@ class OmpCycleChecker {
std::map<std::string, std::int64_t>::iterator it;
bool err{false};
if (cyclestmt.v) {
it = labelNamesandLevels_.find(cyclestmt.v->source.ToString());
err = (it != labelNamesandLevels_.end() && it->second > 0);
it = constructNamesAndLevels_.find(cyclestmt.v->source.ToString());
err = (it != constructNamesAndLevels_.end() && it->second > 0);
} else {
// If there is no label then the cycle statement is associated with the
// closest enclosing DO. Use its level for the checks.
err = cycleLevel_ > 0;
}
if (cycleLevel_ > 0 || err) {
if (err) {
context_.Say(*cycleSource_,
"CYCLE statement to non-innermost associated loop of an OpenMP DO "
"construct"_err_en_US);
Expand All @@ -125,7 +130,7 @@ class OmpCycleChecker {
SemanticsContext &context_;
const parser::CharBlock *cycleSource_;
std::int64_t cycleLevel_;
std::map<std::string, std::int64_t> labelNamesandLevels_;
std::map<std::string, std::int64_t> constructNamesAndLevels_;
};

bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) {
Expand Down
12 changes: 11 additions & 1 deletion flang/runtime/extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
#include <thread>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>

#include <synchapi.h>

inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
Fortran::runtime::Terminator terminator) {
int error{ctime_s(buffer, bufsize, &cur_time)};
Expand Down Expand Up @@ -136,7 +142,11 @@ void RTNAME(Sleep)(std::int64_t seconds) {
if (seconds < 1) {
return;
}
std::this_thread::sleep_for(std::chrono::seconds(seconds));
#if _WIN32
Sleep(seconds * 1000);
#else
sleep(seconds);
#endif
}

// TODO: not supported on Windows
Expand Down
7 changes: 7 additions & 0 deletions flang/test/Lower/CUDA/cuda-data-attribute.cuf
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ end
! CHECK: fir.cuda_free %[[DECL_U]]#1 : !fir.ref<i32> {cuda_attr = #fir.cuda<unified>}
! CHECK: fir.cuda_free %[[DECL_A]]#1 : !fir.ref<!fir.array<10xf32>> {cuda_attr = #fir.cuda<device>}

subroutine dummy(x)
real, target, device :: x
end subroutine

! CHECK: func.func @_QMcuda_varPdummy
! CHECK-NOT: fir.cuda_free

end module


30 changes: 30 additions & 0 deletions libc/src/__support/CPP/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,36 @@ template <typename T> struct Atomic {
int(mem_ord), int(mem_ord));
}

// Atomic compare exchange (separate success and failure memory orders)
bool compare_exchange_strong(
T &expected, T desired, MemoryOrder success_order,
MemoryOrder failure_order,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, false,
static_cast<int>(success_order),
static_cast<int>(failure_order));
}

// Atomic compare exchange (weak version)
bool compare_exchange_weak(
T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, true,
static_cast<int>(mem_ord),
static_cast<int>(mem_ord));
}

// Atomic compare exchange (weak version with separate success and failure
// memory orders)
bool compare_exchange_weak(
T &expected, T desired, MemoryOrder success_order,
MemoryOrder failure_order,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, true,
static_cast<int>(success_order),
static_cast<int>(failure_order));
}

T exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_exchange_n)
Expand Down
4 changes: 2 additions & 2 deletions libc/test/src/math/FModTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
#include "hdr/math_macros.h"

#define TEST_SPECIAL(x, y, expected, dom_err, expected_exception) \
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); \
EXPECT_FP_EQ(expected, f(x, y)); \
EXPECT_MATH_ERRNO((dom_err) ? EDOM : 0); \
EXPECT_FP_EXCEPTION(expected_exception); \
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT)
EXPECT_FP_EXCEPTION(expected_exception)

#define TEST_REGULAR(x, y, expected) TEST_SPECIAL(x, y, expected, false, 0)

Expand Down
Loading