https://github.com/badumbatish updated https://github.com/llvm/llvm-project/pull/169081
>From d015af2cb7ca61a0445a78781b31178e3f6fa9f2 Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Thu, 20 Nov 2025 05:23:58 -0800 Subject: [PATCH 1/5] Skeleton --- .../include/clang/CIR/Sema/CIRAnalysisKind.h | 117 ++++++++++++ .../clang/CIR/Sema/FallThroughWarning.h | 66 +++++++ .../include/clang/Frontend/FrontendOptions.h | 3 + clang/include/clang/Options/Options.td | 5 + clang/lib/CIR/CMakeLists.txt | 1 + clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 36 ++++ clang/lib/CIR/FrontendAction/CMakeLists.txt | 1 + clang/lib/CIR/Sema/CIRAnalysisKind.cpp | 67 +++++++ clang/lib/CIR/Sema/CMakeLists.txt | 19 ++ clang/lib/CIR/Sema/FallThroughWarning.cpp | 170 ++++++++++++++++++ 10 files changed, 485 insertions(+) create mode 100644 clang/include/clang/CIR/Sema/CIRAnalysisKind.h create mode 100644 clang/include/clang/CIR/Sema/FallThroughWarning.h create mode 100644 clang/lib/CIR/Sema/CIRAnalysisKind.cpp create mode 100644 clang/lib/CIR/Sema/CMakeLists.txt create mode 100644 clang/lib/CIR/Sema/FallThroughWarning.cpp diff --git a/clang/include/clang/CIR/Sema/CIRAnalysisKind.h b/clang/include/clang/CIR/Sema/CIRAnalysisKind.h new file mode 100644 index 0000000000000..304acfcf433db --- /dev/null +++ b/clang/include/clang/CIR/Sema/CIRAnalysisKind.h @@ -0,0 +1,117 @@ +//===--- CIRAnalysisKind.h - CIR Analysis Pass Kinds -----------*- 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the CIR analysis pass kinds enum and related utilities. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H +#define LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> +#include <string> +#include <vector> + +namespace cir { + +/// Enumeration of available CIR semantic analysis passes +enum class CIRAnalysisKind : unsigned { + Unrecognized = 0, + FallThrough = 1 << 0, // Fallthrough warning analysis + UnreachableCode = 1 << 1, // Unreachable code detection + NullCheck = 1 << 2, // Null pointer checks + UninitializedVar = 1 << 3, // Uninitialized variable detection + // Add more analysis passes here as needed +}; + +/// A set of CIR analysis passes (bitmask) +class CIRAnalysisSet { + unsigned mask = 0; + +public: + CIRAnalysisSet() = default; + explicit CIRAnalysisSet(CIRAnalysisKind kind) + : mask(static_cast<unsigned>(kind)) {} + explicit CIRAnalysisSet(unsigned mask) : mask(mask) {} + + /// Check if a specific analysis is enabled + bool has(CIRAnalysisKind kind) const { + return (mask & static_cast<unsigned>(kind)) != 0; + } + + /// Enable a specific analysis + void enable(CIRAnalysisKind kind) { + mask |= static_cast<unsigned>(kind); + } + + /// Disable a specific analysis + void disable(CIRAnalysisKind kind) { + mask &= ~static_cast<unsigned>(kind); + } + + /// Check if any analysis is enabled + bool hasAny() const { return mask != 0; } + + /// Check if no analysis is enabled + bool empty() const { return mask == 0; } + + /// Get the raw mask value + unsigned getMask() const { return mask; } + + /// Union with another set + CIRAnalysisSet &operator|=(const CIRAnalysisSet &other) { + mask |= other.mask; + return *this; + } + + /// Union operator + CIRAnalysisSet operator|(const CIRAnalysisSet &other) const { + return CIRAnalysisSet(mask | other.mask); + } + + /// Intersection with another set + CIRAnalysisSet &operator&=(const CIRAnalysisSet &other) { + mask &= other.mask; + return *this; + } + + /// Intersection operator + CIRAnalysisSet operator&(const CIRAnalysisSet &other) const { + return CIRAnalysisSet(mask & other.mask); + } + + bool operator==(const CIRAnalysisSet &other) const { + return mask == other.mask; + } + + bool operator!=(const CIRAnalysisSet &other) const { + return mask != other.mask; + } + + /// Print the analysis set to an output stream + void print(llvm::raw_ostream &OS) const; +}; + +/// Parse a single analysis name into a CIRAnalysisKind +/// Returns std::nullopt if the name is not recognized +CIRAnalysisKind parseCIRAnalysisKind(llvm::StringRef name); + +/// Parse a list of analysis names (from command line) into a CIRAnalysisSet +/// Handles comma and semicolon separators +/// Invalid names are ignored and optionally reported via InvalidNames +CIRAnalysisSet parseCIRAnalysisList( + const std::vector<std::string> &analysisList, + llvm::SmallVectorImpl<std::string> *invalidNames = nullptr); + +} // namespace cir + +#endif // LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H diff --git a/clang/include/clang/CIR/Sema/FallThroughWarning.h b/clang/include/clang/CIR/Sema/FallThroughWarning.h new file mode 100644 index 0000000000000..808cf43f92ce0 --- /dev/null +++ b/clang/include/clang/CIR/Sema/FallThroughWarning.h @@ -0,0 +1,66 @@ +//===--- FallThroughWarning.h - CIR Fall-Through Analysis ------*- 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the FallThroughWarningPass for CIR fall-through analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H +#define LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H + +#include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/AnalysisBasedWarnings.h" + +#include "mlir/IR/Block.h" +#include "mlir/IR/Operation.h" +#include "mlir/Support/LLVM.h" +namespace cir { +class FuncOp; +} // namespace cir + +namespace clang { +class Sema; + +enum ControlFlowKind { + UnknownFallThrough, + NeverFallThrough, + MaybeFallThrough, + AlwaysFallThrough, + NeverFallThroughOrReturn +}; + +/// Configuration for fall-through diagnostics +struct CheckFallThroughDiagnostics { + unsigned diagFallThrough = 0; + unsigned diagReturn = 0; + unsigned diagFallThroughAttr = 0; + unsigned funKind = 0; + SourceLocation funcLoc; + + bool checkDiagnostics(DiagnosticsEngine &d, bool returnsVoid, + bool hasNoReturn) const; +}; + +/// Pass for analyzing fall-through behavior in CIR functions +class FallThroughWarningPass { +public: + FallThroughWarningPass() = default; + + /// Check fall-through behavior for a CIR function body + void checkFallThroughForFuncBody(Sema &s, cir::FuncOp cfg, QualType blockType, + const CheckFallThroughDiagnostics &cd); + ControlFlowKind checkFallThrough(cir::FuncOp cfg); + mlir::DenseSet<mlir::Block *> getLiveSet(cir::FuncOp cfg); +}; + +} // namespace clang + +#endif // LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index c919a53ae089e..a152309212bf8 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -420,6 +420,9 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned ClangIRDisableCIRVerifier : 1; + /// List of ClangIR semantic analysis passes to enable + std::vector<std::string> ClangIRAnalysisList; + CodeCompleteOptions CodeCompleteOpts; /// Specifies the output format of the AST. diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 2f7434d8afe11..16821a7b6f230 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -3150,6 +3150,11 @@ def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">, HelpText<"ClangIR: Disable MLIR module verifier">, MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>; +def fclangir_analysis_EQ : CommaJoined<["-"], "fclangir-analysis=">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable ClangIR semantic analysis passes. Pass comma or semicolon separated list of analysis names">, + MarshallingInfoStringVector<FrontendOpts<"ClangIRAnalysisList">>; + defm clangir : BoolFOption<"clangir", FrontendOpts<"UseClangIRPipeline">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">, diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index 7bdf3fcc59035..f86ad0933bce6 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(CodeGen) add_subdirectory(FrontendAction) add_subdirectory(Interfaces) add_subdirectory(Lowering) +add_subdirectory(Sema) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 67bb5657d4001..fb86b5f848628 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -10,12 +10,20 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/IR/OwningOpRef.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/CIR/CIRGenerator.h" #include "clang/CIR/CIRToCIRPasses.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/LowerToLLVM.h" +#include "clang/CIR/Sema/CIRAnalysisKind.h" +#include "clang/CIR/Sema/FallThroughWarning.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/AnalysisBasedWarnings.h" +#include "clang/Sema/Sema.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" using namespace cir; using namespace clang; @@ -108,6 +116,34 @@ class CIRGenConsumer : public clang::ASTConsumer { mlir::ModuleOp MlirModule = Gen->getModule(); mlir::MLIRContext &MlirCtx = Gen->getMLIRContext(); + // Run CIR analysis passes if requested + if (!FEOptions.ClangIRAnalysisList.empty()) { + CIRAnalysisSet AnalysisSet = parseCIRAnalysisList(FEOptions.ClangIRAnalysisList); + AnalysisSet.print(llvm::errs()); + + if (AnalysisSet.has(CIRAnalysisKind::FallThrough)) { + // Get Sema for diagnostics + if (CI.hasSema()) { + Sema &S = CI.getSema(); + FallThroughWarningPass FallThroughPass; + + // Iterate over all functions in the CIR module + MlirModule.walk([&](cir::FuncOp FuncOp) { + // TODO: Get the proper QualType for the function + // For now, use an invalid QualType as placeholder + + QualType FuncType; + + // Set up diagnostics configuration + CheckFallThroughDiagnostics Diags; + + // Run fall-through analysis on this function + FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, Diags); + }); + } + } + } + if (!FEOptions.ClangIRDisablePasses) { // Setup and run CIR pipeline. if (runCIRToCIRPasses(MlirModule, MlirCtx, C, diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt index 50d6ea7108ce1..f43ef2d8cbbf5 100644 --- a/clang/lib/CIR/FrontendAction/CMakeLists.txt +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangCIRFrontendAction clangBasic clangFrontend clangCIR + clangCIRSema clangCIRLoweringCommon clangCIRLoweringDirectToLLVM clangCodeGen diff --git a/clang/lib/CIR/Sema/CIRAnalysisKind.cpp b/clang/lib/CIR/Sema/CIRAnalysisKind.cpp new file mode 100644 index 0000000000000..3ae24979cfe68 --- /dev/null +++ b/clang/lib/CIR/Sema/CIRAnalysisKind.cpp @@ -0,0 +1,67 @@ +//===--- CIRAnalysisKind.cpp - CIR Analysis Pass Kinds -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/Sema/CIRAnalysisKind.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +namespace cir { + +CIRAnalysisKind parseCIRAnalysisKind(llvm::StringRef name) { + auto parseResult = llvm::StringSwitch<CIRAnalysisKind>(name) + .Case("fallthrough", CIRAnalysisKind::FallThrough) + .Case("fall-through", CIRAnalysisKind::FallThrough) + .Default(CIRAnalysisKind::Unrecognized); + + return parseResult; +} + + +CIRAnalysisSet parseCIRAnalysisList( + const std::vector<std::string> &analysisList, + llvm::SmallVectorImpl<std::string> *invalidNames) { + CIRAnalysisSet result; + + for (const std::string &item : analysisList) { + llvm::StringRef remaining = item; + CIRAnalysisKind parseKind = parseCIRAnalysisKind(remaining); + if (parseKind == CIRAnalysisKind::Unrecognized) { + llvm::errs() << "Unrecognized CIR analysis option: " << remaining << "\n"; + continue; + } + result.enable(parseKind); + } + + return result; +} + +void CIRAnalysisSet::print(llvm::raw_ostream &OS) const { + if (empty()) { + OS << "none"; + return; + } + + bool first = true; + auto printIfEnabled = [&](CIRAnalysisKind kind, llvm::StringRef name) { + if (has(kind)) { + if (!first) + OS << ", "; + OS << name; + first = false; + } + }; + + printIfEnabled(CIRAnalysisKind::FallThrough, "fallthrough"); + printIfEnabled(CIRAnalysisKind::UnreachableCode, "unreachable-code"); + printIfEnabled(CIRAnalysisKind::NullCheck, "null-check"); + printIfEnabled(CIRAnalysisKind::UninitializedVar, "uninitialized-var"); +} + +} // namespace cir diff --git a/clang/lib/CIR/Sema/CMakeLists.txt b/clang/lib/CIR/Sema/CMakeLists.txt new file mode 100644 index 0000000000000..acf49742d8e4c --- /dev/null +++ b/clang/lib/CIR/Sema/CMakeLists.txt @@ -0,0 +1,19 @@ +add_clang_library(clangCIRSema + FallThroughWarning.cpp + CIRAnalysisKind.cpp + + DEPENDS + MLIRCIRPassIncGen + + LINK_LIBS PUBLIC + clangAST + clangBasic + + MLIRAnalysis + MLIRIR + MLIRPass + MLIRTransformUtils + + MLIRCIR + MLIRCIRInterfaces +) diff --git a/clang/lib/CIR/Sema/FallThroughWarning.cpp b/clang/lib/CIR/Sema/FallThroughWarning.cpp new file mode 100644 index 0000000000000..5fdc9ffc083cb --- /dev/null +++ b/clang/lib/CIR/Sema/FallThroughWarning.cpp @@ -0,0 +1,170 @@ +#include "clang/CIR/Sema/FallThroughWarning.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/raw_ostream.h" + +using namespace mlir; +using namespace cir; +using namespace clang; + +namespace clang { + +//===----------------------------------------------------------------------===// +// Check for missing return value. +//===----------------------------------------------------------------------===// + +bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, + bool returnsVoid, + bool hasNoReturn) const { + if (funKind == diag::FalloffFunctionKind::Function) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn || + d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc)) && + (!returnsVoid || + d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); + } + if (funKind == diag::FalloffFunctionKind::Coroutine) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn); + } + // For blocks / lambdas. + return returnsVoid && !hasNoReturn; +} + +// TODO: Add a class for fall through config later + +void FallThroughWarningPass::checkFallThroughForFuncBody( + Sema &s, cir::FuncOp cfg, QualType blockType, + const CheckFallThroughDiagnostics &cd) { + + llvm::errs() << "Hello world, you're in CIR sema analysis\n"; + bool returnsVoid = false; + bool hasNoReturn = false; + + // Supposedly all function in cir is FuncOp + // 1. If normal function (FunctionDecl), check if it's coroutine. + // 1a. if coroutine -> check the fallthrough handler (idk what this means, + // TODO for now) + if (cfg.getCoroutine()) { + // TODO: Let's not worry about coroutine for now + } else + returnsVoid = isa<cir::VoidType>(cfg.getFunctionType().getReturnType()); + + // TODO: Do we need to check for InferredNoReturnAttr just like in OG? + hasNoReturn = cfg.getFunctionType().getReturnTypes().empty(); + + DiagnosticsEngine &diags = s.getDiagnostics(); + if (cd.checkDiagnostics(diags, returnsVoid, hasNoReturn)) { + return; + } + + // cpu_dispatch functions permit empty function bodies for ICC compatibility. + // TODO: Do we have isCPUDispatchMultiVersion? + checkFallThrough(cfg); +} + +mlir::DenseSet<mlir::Block *> +FallThroughWarningPass::getLiveSet(cir::FuncOp cfg) { + mlir::DenseSet<mlir::Block *> liveSet; + if (cfg.getBody().empty()) + return liveSet; + + auto &first = cfg.getBody().getBlocks().front(); + + for (auto &block : cfg.getBody()) { + if (block.isReachable(&first)) + liveSet.insert(&block); + } + return liveSet; +} + +ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { + + assert(cfg && "there can't be a null func op"); + + // TODO: Is no CFG akin to a declaration? + if (cfg.isDeclaration()) { + return UnknownFallThrough; + } + + mlir::DenseSet<mlir::Block *> liveSet = this->getLiveSet(cfg); + + unsigned count = liveSet.size(); + + bool hasLiveReturn = false; + bool hasFakeEdge = false; + bool hasPlainEdge = false; + bool hasAbnormalEdge = false; + + auto &exitBlock = cfg.getBody().back(); + // INFO: in OG clang CFG, they have an empty exit block, so when they query + // pred of exit OG, they get all exit blocks + // + // I guess in CIR, we can pretend exit blocks are all blocks that have no + // successor? + for (mlir::Block &pred : cfg.getBody().getBlocks()) { + if (!liveSet.contains(&pred)) + continue; + + // We consider no predecessors as 'exit blocks' + if (!pred.hasNoSuccessors()) + continue; + + if (!pred.mightHaveTerminator()) + continue; + + mlir::Operation *term = pred.getTerminator(); + if (isa<cir::ReturnOp>(term)) { + hasAbnormalEdge = true; + continue; + } + + // INFO: In OG, we'll be looking for destructor since it can appear past + // return but i guess not in CIR? In this case we'll only be examining the + // terminator + + if (isa<cir::TryOp>(term)) { + hasAbnormalEdge = true; + continue; + } + + // INFO: OG clang has this equals true whenever ri == re, which means this + // is true only when a block only has the terminator, or its size is 1. + hasPlainEdge = std::distance(pred.begin(), pred.end()) == 1; + + if (isa<cir::ReturnOp>(term)) { + hasLiveReturn = true; + continue; + } + if (isa<cir::TryOp>(term)) { + hasLiveReturn = true; + continue; + } + + // TODO: Maybe one day throw will be terminator? + // + // TODO: We need to add a microsoft inline assembly enum + + // TODO: We don't concer with try op either since it's not terminator + + hasPlainEdge = true; + } + + if (!hasPlainEdge) { + if (hasLiveReturn) + return NeverFallThrough; + return NeverFallThroughOrReturn; + } + if (hasAbnormalEdge || hasFakeEdge || hasLiveReturn) + return MaybeFallThrough; + // This says AlwaysFallThrough for calls to functions that are not marked + // noreturn, that don't return. If people would like this warning to be more + // accurate, such functions should be marked as noreturn. + return AlwaysFallThrough; +} +} // namespace clang >From 0eafeed1b29169fa5b8100084da13086855ca263 Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Thu, 20 Nov 2025 19:05:04 -0800 Subject: [PATCH 2/5] Fix reachable behaviors --- clang/lib/CIR/Sema/FallThroughWarning.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/Sema/FallThroughWarning.cpp b/clang/lib/CIR/Sema/FallThroughWarning.cpp index 5fdc9ffc083cb..dd22317fb48ba 100644 --- a/clang/lib/CIR/Sema/FallThroughWarning.cpp +++ b/clang/lib/CIR/Sema/FallThroughWarning.cpp @@ -77,7 +77,7 @@ FallThroughWarningPass::getLiveSet(cir::FuncOp cfg) { auto &first = cfg.getBody().getBlocks().front(); for (auto &block : cfg.getBody()) { - if (block.isReachable(&first)) + if (first.isReachable(&block)) liveSet.insert(&block); } return liveSet; >From 0e416f49e38bfef8b7a4520424e1495e17b78f86 Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Sun, 23 Nov 2025 01:33:00 -0800 Subject: [PATCH 3/5] Rename sema/ to analysis/ and add new test --- .../CIR/{Sema => Analysis}/CIRAnalysisKind.h | 0 .../{Sema => Analysis}/FallThroughWarning.h | 6 +- .../{Sema => Analysis}/CIRAnalysisKind.cpp | 2 +- .../lib/CIR/{Sema => Analysis}/CMakeLists.txt | 0 .../{Sema => Analysis}/FallThroughWarning.cpp | 58 ++++++++++++++++++- clang/lib/CIR/CMakeLists.txt | 2 +- clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 4 +- clang/test/CIR/Analysis/fallthrough_1.c | 33 +++++++++++ clang/test/CIR/Analysis/fallthrough_2.c | 37 ++++++++++++ 9 files changed, 132 insertions(+), 10 deletions(-) rename clang/include/clang/CIR/{Sema => Analysis}/CIRAnalysisKind.h (100%) rename clang/include/clang/CIR/{Sema => Analysis}/FallThroughWarning.h (93%) rename clang/lib/CIR/{Sema => Analysis}/CIRAnalysisKind.cpp (97%) rename clang/lib/CIR/{Sema => Analysis}/CMakeLists.txt (100%) rename clang/lib/CIR/{Sema => Analysis}/FallThroughWarning.cpp (74%) create mode 100644 clang/test/CIR/Analysis/fallthrough_1.c create mode 100644 clang/test/CIR/Analysis/fallthrough_2.c diff --git a/clang/include/clang/CIR/Sema/CIRAnalysisKind.h b/clang/include/clang/CIR/Analysis/CIRAnalysisKind.h similarity index 100% rename from clang/include/clang/CIR/Sema/CIRAnalysisKind.h rename to clang/include/clang/CIR/Analysis/CIRAnalysisKind.h diff --git a/clang/include/clang/CIR/Sema/FallThroughWarning.h b/clang/include/clang/CIR/Analysis/FallThroughWarning.h similarity index 93% rename from clang/include/clang/CIR/Sema/FallThroughWarning.h rename to clang/include/clang/CIR/Analysis/FallThroughWarning.h index 808cf43f92ce0..3f87b3f867d65 100644 --- a/clang/include/clang/CIR/Sema/FallThroughWarning.h +++ b/clang/include/clang/CIR/Analysis/FallThroughWarning.h @@ -39,9 +39,9 @@ enum ControlFlowKind { /// Configuration for fall-through diagnostics struct CheckFallThroughDiagnostics { - unsigned diagFallThrough = 0; - unsigned diagReturn = 0; - unsigned diagFallThroughAttr = 0; + unsigned diagFallThroughHasNoReturn = 0; + unsigned diagFallThroughReturnsNonVoid = 0; + unsigned diagNeverFallThroughOrReturn = 0; unsigned funKind = 0; SourceLocation funcLoc; diff --git a/clang/lib/CIR/Sema/CIRAnalysisKind.cpp b/clang/lib/CIR/Analysis/CIRAnalysisKind.cpp similarity index 97% rename from clang/lib/CIR/Sema/CIRAnalysisKind.cpp rename to clang/lib/CIR/Analysis/CIRAnalysisKind.cpp index 3ae24979cfe68..f27f8dbeb731d 100644 --- a/clang/lib/CIR/Sema/CIRAnalysisKind.cpp +++ b/clang/lib/CIR/Analysis/CIRAnalysisKind.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/CIR/Sema/CIRAnalysisKind.h" +#include "clang/CIR/Analysis/CIRAnalysisKind.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" diff --git a/clang/lib/CIR/Sema/CMakeLists.txt b/clang/lib/CIR/Analysis/CMakeLists.txt similarity index 100% rename from clang/lib/CIR/Sema/CMakeLists.txt rename to clang/lib/CIR/Analysis/CMakeLists.txt diff --git a/clang/lib/CIR/Sema/FallThroughWarning.cpp b/clang/lib/CIR/Analysis/FallThroughWarning.cpp similarity index 74% rename from clang/lib/CIR/Sema/FallThroughWarning.cpp rename to clang/lib/CIR/Analysis/FallThroughWarning.cpp index dd22317fb48ba..d437eb6c377c4 100644 --- a/clang/lib/CIR/Sema/FallThroughWarning.cpp +++ b/clang/lib/CIR/Analysis/FallThroughWarning.cpp @@ -1,4 +1,4 @@ -#include "clang/CIR/Sema/FallThroughWarning.h" +#include "clang/CIR/Analysis/FallThroughWarning.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/SourceLocation.h" @@ -6,6 +6,7 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/Sema/Sema.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace mlir; @@ -14,6 +15,36 @@ using namespace clang; namespace clang { +//===----------------------------------------------------------------------===// +// Helper function to lookup a Decl by name from ASTContext +//===----------------------------------------------------------------------===// + +/// Lookup a declaration by name in the translation unit. +/// \param Context The ASTContext to search in +/// \param Name The name of the declaration to find +/// \return The found Decl, or nullptr if not found + +/// WARN: I have to say, we only use this because a lot of the time, attribute +/// that we might need are not port to CIR currently so this function is +/// basically a crutch for that +static Decl *getDeclByName(ASTContext &context, StringRef name) { + // Get the identifier for the name + IdentifierInfo *ii = &context.Idents.get(name); + + // Create a DeclarationName from the identifier + DeclarationName dName(ii); + + // Lookup in the translation unit + TranslationUnitDecl *tu = context.getTranslationUnitDecl(); + DeclContext::lookup_result result = tu->lookup(dName); + + // Return the first match, or nullptr if not found + if (result.empty()) + return nullptr; + + return result.front(); +} + //===----------------------------------------------------------------------===// // Check for missing return value. //===----------------------------------------------------------------------===// @@ -41,8 +72,11 @@ bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, void FallThroughWarningPass::checkFallThroughForFuncBody( Sema &s, cir::FuncOp cfg, QualType blockType, const CheckFallThroughDiagnostics &cd) { - llvm::errs() << "Hello world, you're in CIR sema analysis\n"; + + auto *d = getDeclByName(s.getASTContext(), cfg.getName()); + assert(d && "we need non null decl"); + bool returnsVoid = false; bool hasNoReturn = false; @@ -65,7 +99,25 @@ void FallThroughWarningPass::checkFallThroughForFuncBody( // cpu_dispatch functions permit empty function bodies for ICC compatibility. // TODO: Do we have isCPUDispatchMultiVersion? - checkFallThrough(cfg); + + switch (ControlFlowKind fallThroughType = checkFallThrough(cfg)) { + case UnknownFallThrough: + case MaybeFallThrough: + case AlwaysFallThrough: + if (hasNoReturn && cd.diagFallThroughHasNoReturn) { + + } else if (!returnsVoid && cd.diagFallThroughReturnsNonVoid) { + + } + break; + case NeverFallThroughOrReturn: + if (returnsVoid && !hasNoReturn && cd.diagNeverFallThroughOrReturn) { + } + break; + + case NeverFallThrough: { + } break; + } } mlir::DenseSet<mlir::Block *> diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index f86ad0933bce6..c698267b04ef5 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -17,4 +17,4 @@ add_subdirectory(CodeGen) add_subdirectory(FrontendAction) add_subdirectory(Interfaces) add_subdirectory(Lowering) -add_subdirectory(Sema) +add_subdirectory(Analysis) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index fb86b5f848628..bcb8e8fbb644d 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -15,8 +15,8 @@ #include "clang/CIR/CIRToCIRPasses.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/LowerToLLVM.h" -#include "clang/CIR/Sema/CIRAnalysisKind.h" -#include "clang/CIR/Sema/FallThroughWarning.h" +#include "clang/CIR/Analysis/CIRAnalysisKind.h" +#include "clang/CIR/Analysis/FallThroughWarning.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Sema/AnalysisBasedWarnings.h" diff --git a/clang/test/CIR/Analysis/fallthrough_1.c b/clang/test/CIR/Analysis/fallthrough_1.c new file mode 100644 index 0000000000000..267745afeba5d --- /dev/null +++ b/clang/test/CIR/Analysis/fallthrough_1.c @@ -0,0 +1,33 @@ +// REQUIRES: false + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fclangir-analysis="fallthrough" -w +// INFO: These test cases are derived from clang/test/Sema/return.c +int unknown(void); + +int test7(void) { + unknown(); +} // expected-warning {{non-void function does not return a value}} + +int test8(void) { + (void)(1 + unknown()); +} // expected-warning {{non-void function does not return a value}} + + + +int test14(void) { + (void)(1 || unknown()); +} // expected-warning {{non-void function does not return a value}} + +int test15(void) { + (void)(0 || unknown()); +} // expected-warning {{non-void function does not return a value}} + +int test16(void) { + (void)(0 && unknown()); +} // expected-warning {{non-void function does not return a value}} + +int test17(void) { + (void)(1 && unknown()); +} // expected-warning {{non-void function does not return a value}} + + diff --git a/clang/test/CIR/Analysis/fallthrough_2.c b/clang/test/CIR/Analysis/fallthrough_2.c new file mode 100644 index 0000000000000..22c399730a4ff --- /dev/null +++ b/clang/test/CIR/Analysis/fallthrough_2.c @@ -0,0 +1,37 @@ +// REQUIRES: false + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fclangir-analysis="fallthrough" -w +// INFO: These test cases are derived from clang/test/Sema/return.c + +int test1(void) { +} // expected-warning {{non-void function does not return a value}} + +int test3(void) { + goto a; + a: ; +} // expected-warning {{non-void function does not return a value}} + +int test20(void) { + int i; + if (i) + return 0; + else if (0) + return 2; +} // expected-warning {{non-void function does not return a value in all control paths}} + +int test22(void) { + int i; + switch (i) default: ; +} // expected-warning {{non-void function does not return a value}} + +int test23(void) { + int i; + switch (i) { + case 0: + return 0; + case 2: + return 2; + } +} // expected-warning {{non-void function does not return a value in all control paths}} + + >From f7aa0ff92dcf744d463e058b921ed2f41fb09fbd Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Sun, 23 Nov 2025 03:50:45 -0800 Subject: [PATCH 4/5] test1 working --- .../clang/CIR/Analysis/FallThroughWarning.h | 10 +- clang/lib/CIR/Analysis/FallThroughWarning.cpp | 228 +++++++++++++++--- clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 15 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 1 + 4 files changed, 215 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/CIR/Analysis/FallThroughWarning.h b/clang/include/clang/CIR/Analysis/FallThroughWarning.h index 3f87b3f867d65..d0213bd677f85 100644 --- a/clang/include/clang/CIR/Analysis/FallThroughWarning.h +++ b/clang/include/clang/CIR/Analysis/FallThroughWarning.h @@ -21,6 +21,7 @@ #include "mlir/IR/Block.h" #include "mlir/IR/Operation.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" #include "mlir/Support/LLVM.h" namespace cir { class FuncOp; @@ -28,7 +29,7 @@ class FuncOp; namespace clang { class Sema; - +Decl *getDeclByName(ASTContext &context, StringRef name); enum ControlFlowKind { UnknownFallThrough, NeverFallThrough, @@ -45,9 +46,16 @@ struct CheckFallThroughDiagnostics { unsigned funKind = 0; SourceLocation funcLoc; + static CheckFallThroughDiagnostics makeForFunction(Sema &s, + const Decl *func); + static CheckFallThroughDiagnostics makeForCoroutine(const Decl *func); + static CheckFallThroughDiagnostics makeForBlock(); + static CheckFallThroughDiagnostics makeForLambda(); bool checkDiagnostics(DiagnosticsEngine &d, bool returnsVoid, bool hasNoReturn) const; }; +/// Check if a return operation returns a phony value (uninitialized __retval) +bool isPhonyReturn(cir::ReturnOp returnOp); /// Pass for analyzing fall-through behavior in CIR functions class FallThroughWarningPass { diff --git a/clang/lib/CIR/Analysis/FallThroughWarning.cpp b/clang/lib/CIR/Analysis/FallThroughWarning.cpp index d437eb6c377c4..d6bee5f826b4c 100644 --- a/clang/lib/CIR/Analysis/FallThroughWarning.cpp +++ b/clang/lib/CIR/Analysis/FallThroughWarning.cpp @@ -27,7 +27,7 @@ namespace clang { /// WARN: I have to say, we only use this because a lot of the time, attribute /// that we might need are not port to CIR currently so this function is /// basically a crutch for that -static Decl *getDeclByName(ASTContext &context, StringRef name) { +Decl *getDeclByName(ASTContext &context, StringRef name) { // Get the identifier for the name IdentifierInfo *ii = &context.Idents.get(name); @@ -45,6 +45,122 @@ static Decl *getDeclByName(ASTContext &context, StringRef name) { return result.front(); } +CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForFunction(Sema &s, + const Decl *func) { + CheckFallThroughDiagnostics d; + d.funcLoc = func->getLocation(); + d.diagFallThroughHasNoReturn = diag::warn_noreturn_has_return_expr; + d.diagFallThroughReturnsNonVoid = diag::warn_falloff_nonvoid; + + // Don't suggest that virtual functions be marked "noreturn", since they + // might be overridden by non-noreturn functions. + bool isVirtualMethod = false; + if (const CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(func)) + isVirtualMethod = method->isVirtual(); + + // Don't suggest that template instantiations be marked "noreturn" + bool isTemplateInstantiation = false; + if (const FunctionDecl *function = dyn_cast<FunctionDecl>(func)) { + isTemplateInstantiation = function->isTemplateInstantiation(); + if (!s.getLangOpts().CPlusPlus && !s.getLangOpts().C99 && + function->isMain()) { + d.diagFallThroughReturnsNonVoid = diag::ext_main_no_return; + } + } + + if (!isVirtualMethod && !isTemplateInstantiation) + d.diagNeverFallThroughOrReturn = diag::warn_suggest_noreturn_function; + + d.funKind = diag::FalloffFunctionKind::Function; + return d; +} + +CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForCoroutine(const Decl *func) { + CheckFallThroughDiagnostics d; + d.funcLoc = func->getLocation(); + d.diagFallThroughReturnsNonVoid = diag::warn_falloff_nonvoid; + d.funKind = diag::FalloffFunctionKind::Coroutine; + return d; +} + +CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForBlock() { + CheckFallThroughDiagnostics D; + D.diagFallThroughHasNoReturn = diag::err_noreturn_has_return_expr; + D.diagFallThroughReturnsNonVoid = diag::err_falloff_nonvoid; + D.funKind = diag::FalloffFunctionKind::Block; + return D; +} + +CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForLambda() { + CheckFallThroughDiagnostics d; + d.diagFallThroughHasNoReturn = diag::err_noreturn_has_return_expr; + d.diagFallThroughReturnsNonVoid = diag::warn_falloff_nonvoid; + d.funKind = diag::FalloffFunctionKind::Lambda; + return d; +} + + +//===----------------------------------------------------------------------===// +// Check for phony return values (returning uninitialized __retval) +//===----------------------------------------------------------------------===// + +/// Check if a return operation returns a phony value. +/// A phony return is when a function returns a value loaded from an +/// uninitialized __retval alloca, which indicates the function doesn't +/// actually return a meaningful value. +/// +/// Example of phony return: +/// \code +/// cir.func @test1() -> !s32i { +/// %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] +/// %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i +/// cir.return %1 : !s32i +/// } +/// \endcode +/// +/// \param returnOp The return operation to check +/// \return true if this is a phony return, false otherwise +bool isPhonyReturn(cir::ReturnOp returnOp) { + if (!returnOp) + return false; + + // Get the returned value - return operations use $input as the operand + if (!returnOp.hasOperand()) + return false; + + auto returnValue = returnOp.getInput()[0]; + + // Check if the return value comes from a load operation + auto loadOp = returnValue.getDefiningOp<cir::LoadOp>(); + if (!loadOp) + return false; + + // Check if the load is from an alloca + auto allocaOp = loadOp.getAddr().getDefiningOp<cir::AllocaOp>(); + if (!allocaOp) + return false; + + // Check if the alloca is named "__retval" + auto name = allocaOp.getName(); + if (name != "__retval") + return false; + + // Check if the alloca has any stores to it (if not, it's uninitialized) + // We need to search for store operations that write to this alloca + mlir::Value allocaResult = allocaOp.getResult(); + + for (auto *user : allocaResult.getUsers()) { + if (auto storeOp = dyn_cast<cir::StoreOp>(user)) { + // If there's a store to this alloca, it's not phony + // (assuming the store happens before the load in control flow) + return false; + } + } + + // No stores found to __retval alloca - this is a phony return + return true; +} + //===----------------------------------------------------------------------===// // Check for missing return value. //===----------------------------------------------------------------------===// @@ -52,19 +168,21 @@ static Decl *getDeclByName(ASTContext &context, StringRef name) { bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, bool returnsVoid, bool hasNoReturn) const { - if (funKind == diag::FalloffFunctionKind::Function) { - return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && - (!hasNoReturn || - d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc)) && - (!returnsVoid || - d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); - } - if (funKind == diag::FalloffFunctionKind::Coroutine) { - return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && - (!hasNoReturn); - } - // For blocks / lambdas. - return returnsVoid && !hasNoReturn; + if (funKind == diag::FalloffFunctionKind::Function) { + return (returnsVoid || + d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc) || + !hasNoReturn) && + (!returnsVoid || + d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); + } + if (funKind == diag::FalloffFunctionKind::Coroutine) { + return (returnsVoid || + d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn); + } + // For blocks / lambdas. + return returnsVoid && !hasNoReturn; } // TODO: Add a class for fall through config later @@ -72,41 +190,83 @@ bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, void FallThroughWarningPass::checkFallThroughForFuncBody( Sema &s, cir::FuncOp cfg, QualType blockType, const CheckFallThroughDiagnostics &cd) { - llvm::errs() << "Hello world, you're in CIR sema analysis\n"; auto *d = getDeclByName(s.getASTContext(), cfg.getName()); + auto *body = d->getBody(); assert(d && "we need non null decl"); bool returnsVoid = false; bool hasNoReturn = false; + SourceLocation lBrace = body->getBeginLoc(), rBrace = body->getEndLoc(); // Supposedly all function in cir is FuncOp // 1. If normal function (FunctionDecl), check if it's coroutine. // 1a. if coroutine -> check the fallthrough handler (idk what this means, // TODO for now) - if (cfg.getCoroutine()) { - // TODO: Let's not worry about coroutine for now - } else - returnsVoid = isa<cir::VoidType>(cfg.getFunctionType().getReturnType()); - - // TODO: Do we need to check for InferredNoReturnAttr just like in OG? - hasNoReturn = cfg.getFunctionType().getReturnTypes().empty(); + if (const auto *fd = dyn_cast<FunctionDecl>(d)) { + if (const auto *cBody = dyn_cast<CoroutineBodyStmt>(d->getBody())) + returnsVoid = cBody->getFallthroughHandler() != nullptr; + else + returnsVoid = fd->getReturnType()->isVoidType(); + hasNoReturn = fd->isNoReturn() || fd->hasAttr<InferredNoReturnAttr>(); + } + else if (const auto *md = dyn_cast<ObjCMethodDecl>(d)) { + returnsVoid = md->getReturnType()->isVoidType(); + hasNoReturn = md->hasAttr<NoReturnAttr>(); + } + else if (isa<BlockDecl>(d)) { + if (const FunctionType *ft = + blockType->getPointeeType()->getAs<FunctionType>()) { + if (ft->getReturnType()->isVoidType()) + returnsVoid = true; + if (ft->getNoReturnAttr()) + hasNoReturn = true; + } + } DiagnosticsEngine &diags = s.getDiagnostics(); - if (cd.checkDiagnostics(diags, returnsVoid, hasNoReturn)) { - return; - } + + // Short circuit for compilation speed. + if (cd.checkDiagnostics(diags, returnsVoid, hasNoReturn)) + return; // cpu_dispatch functions permit empty function bodies for ICC compatibility. // TODO: Do we have isCPUDispatchMultiVersion? switch (ControlFlowKind fallThroughType = checkFallThrough(cfg)) { case UnknownFallThrough: + [[fallthrough]]; case MaybeFallThrough: + [[fallthrough]]; case AlwaysFallThrough: if (hasNoReturn && cd.diagFallThroughHasNoReturn) { } else if (!returnsVoid && cd.diagFallThroughReturnsNonVoid) { + // If the final statement is a call to an always-throwing function, + // don't warn about the fall-through. + if (d->getAsFunction()) { + if (const auto *cs = dyn_cast<CompoundStmt>(body); + cs && !cs->body_empty()) { + const Stmt *lastStmt = cs->body_back(); + // Unwrap ExprWithCleanups if necessary. + if (const auto *ewc = dyn_cast<ExprWithCleanups>(lastStmt)) { + lastStmt = ewc->getSubExpr(); + } + if (const auto *ce = dyn_cast<CallExpr>(lastStmt)) { + if (const FunctionDecl *callee = ce->getDirectCallee(); + callee && callee->hasAttr<InferredNoReturnAttr>()) { + return; // Don't warn about fall-through. + } + } + // Direct throw. + if (isa<CXXThrowExpr>(lastStmt)) { + return; // Don't warn about fall-through. + } + } + } + bool notInAllControlPaths = fallThroughType == MaybeFallThrough; + s.Diag(rBrace, cd.diagFallThroughReturnsNonVoid) + << cd.funKind << notInAllControlPaths; } break; @@ -129,7 +289,7 @@ FallThroughWarningPass::getLiveSet(cir::FuncOp cfg) { auto &first = cfg.getBody().getBlocks().front(); for (auto &block : cfg.getBody()) { - if (first.isReachable(&block)) + if (&first == &block || first.isReachable(&block)) liveSet.insert(&block); } return liveSet; @@ -171,10 +331,8 @@ ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { continue; mlir::Operation *term = pred.getTerminator(); - if (isa<cir::ReturnOp>(term)) { - hasAbnormalEdge = true; - continue; - } + + // TODO: hasNoReturnElement() in OG here, not sure how to work it in here yet // INFO: In OG, we'll be looking for destructor since it can appear past // return but i guess not in CIR? In this case we'll only be examining the @@ -189,9 +347,11 @@ ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { // is true only when a block only has the terminator, or its size is 1. hasPlainEdge = std::distance(pred.begin(), pred.end()) == 1; - if (isa<cir::ReturnOp>(term)) { - hasLiveReturn = true; - continue; + if (auto returnOp = dyn_cast<cir::ReturnOp>(term)) { + if (!isPhonyReturn(returnOp)) { + hasLiveReturn = true; + continue; + } } if (isa<cir::TryOp>(term)) { hasLiveReturn = true; @@ -217,6 +377,8 @@ ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { // This says AlwaysFallThrough for calls to functions that are not marked // noreturn, that don't return. If people would like this warning to be more // accurate, such functions should be marked as noreturn. + // + // llvm_unreachable(""); return AlwaysFallThrough; } } // namespace clang diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index bcb8e8fbb644d..1b06c311447af 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -119,10 +119,8 @@ class CIRGenConsumer : public clang::ASTConsumer { // Run CIR analysis passes if requested if (!FEOptions.ClangIRAnalysisList.empty()) { CIRAnalysisSet AnalysisSet = parseCIRAnalysisList(FEOptions.ClangIRAnalysisList); - AnalysisSet.print(llvm::errs()); if (AnalysisSet.has(CIRAnalysisKind::FallThrough)) { - // Get Sema for diagnostics if (CI.hasSema()) { Sema &S = CI.getSema(); FallThroughWarningPass FallThroughPass; @@ -135,10 +133,17 @@ class CIRGenConsumer : public clang::ASTConsumer { QualType FuncType; // Set up diagnostics configuration - CheckFallThroughDiagnostics Diags; - + // INFO: This is not full + Decl *D = getDeclByName(S.getASTContext(), FuncOp.getName()); + const CheckFallThroughDiagnostics &CD = + (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::makeForBlock() + : (isa<CXXMethodDecl>(D) && + cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call && + cast<CXXMethodDecl>(D)->getParent()->isLambda()) + ? CheckFallThroughDiagnostics::makeForLambda() + : CheckFallThroughDiagnostics::makeForFunction(S, D)); // Run fall-through analysis on this function - FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, Diags); + FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, CD); }); } } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 41a98323450e4..a2f7f61b57d95 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -53,6 +53,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include <algorithm> #include <deque> #include <iterator> >From e4a96eff701bd35fe07a4316c3c62a33658ac99d Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Sun, 23 Nov 2025 04:53:49 -0800 Subject: [PATCH 5/5] Unknown should break --- .../clang/CIR/Analysis/FallThroughWarning.h | 5 +- clang/lib/CIR/Analysis/FallThroughWarning.cpp | 54 +++++++++---------- clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 11 ++-- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/CIR/Analysis/FallThroughWarning.h b/clang/include/clang/CIR/Analysis/FallThroughWarning.h index d0213bd677f85..f78a07d5212c3 100644 --- a/clang/include/clang/CIR/Analysis/FallThroughWarning.h +++ b/clang/include/clang/CIR/Analysis/FallThroughWarning.h @@ -21,8 +21,8 @@ #include "mlir/IR/Block.h" #include "mlir/IR/Operation.h" -#include "clang/CIR/Dialect/IR/CIRDialect.h" #include "mlir/Support/LLVM.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" namespace cir { class FuncOp; } // namespace cir @@ -46,8 +46,7 @@ struct CheckFallThroughDiagnostics { unsigned funKind = 0; SourceLocation funcLoc; - static CheckFallThroughDiagnostics makeForFunction(Sema &s, - const Decl *func); + static CheckFallThroughDiagnostics makeForFunction(Sema &s, const Decl *func); static CheckFallThroughDiagnostics makeForCoroutine(const Decl *func); static CheckFallThroughDiagnostics makeForBlock(); static CheckFallThroughDiagnostics makeForLambda(); diff --git a/clang/lib/CIR/Analysis/FallThroughWarning.cpp b/clang/lib/CIR/Analysis/FallThroughWarning.cpp index d6bee5f826b4c..0b4ddfa5cd113 100644 --- a/clang/lib/CIR/Analysis/FallThroughWarning.cpp +++ b/clang/lib/CIR/Analysis/FallThroughWarning.cpp @@ -45,8 +45,8 @@ Decl *getDeclByName(ASTContext &context, StringRef name) { return result.front(); } -CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForFunction(Sema &s, - const Decl *func) { +CheckFallThroughDiagnostics +CheckFallThroughDiagnostics::makeForFunction(Sema &s, const Decl *func) { CheckFallThroughDiagnostics d; d.funcLoc = func->getLocation(); d.diagFallThroughHasNoReturn = diag::warn_noreturn_has_return_expr; @@ -75,7 +75,8 @@ CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForFunction(Sema &s return d; } -CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForCoroutine(const Decl *func) { +CheckFallThroughDiagnostics +CheckFallThroughDiagnostics::makeForCoroutine(const Decl *func) { CheckFallThroughDiagnostics d; d.funcLoc = func->getLocation(); d.diagFallThroughReturnsNonVoid = diag::warn_falloff_nonvoid; @@ -99,7 +100,6 @@ CheckFallThroughDiagnostics CheckFallThroughDiagnostics::makeForLambda() { return d; } - //===----------------------------------------------------------------------===// // Check for phony return values (returning uninitialized __retval) //===----------------------------------------------------------------------===// @@ -168,21 +168,19 @@ bool isPhonyReturn(cir::ReturnOp returnOp) { bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, bool returnsVoid, bool hasNoReturn) const { - if (funKind == diag::FalloffFunctionKind::Function) { - return (returnsVoid || - d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && - (d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc) || - !hasNoReturn) && - (!returnsVoid || - d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); - } - if (funKind == diag::FalloffFunctionKind::Coroutine) { - return (returnsVoid || - d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && - (!hasNoReturn); - } - // For blocks / lambdas. - return returnsVoid && !hasNoReturn; + if (funKind == diag::FalloffFunctionKind::Function) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc) || + !hasNoReturn) && + (!returnsVoid || + d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); + } + if (funKind == diag::FalloffFunctionKind::Coroutine) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn); + } + // For blocks / lambdas. + return returnsVoid && !hasNoReturn; } // TODO: Add a class for fall through config later @@ -209,14 +207,12 @@ void FallThroughWarningPass::checkFallThroughForFuncBody( else returnsVoid = fd->getReturnType()->isVoidType(); hasNoReturn = fd->isNoReturn() || fd->hasAttr<InferredNoReturnAttr>(); - } - else if (const auto *md = dyn_cast<ObjCMethodDecl>(d)) { + } else if (const auto *md = dyn_cast<ObjCMethodDecl>(d)) { returnsVoid = md->getReturnType()->isVoidType(); hasNoReturn = md->hasAttr<NoReturnAttr>(); - } - else if (isa<BlockDecl>(d)) { + } else if (isa<BlockDecl>(d)) { if (const FunctionType *ft = - blockType->getPointeeType()->getAs<FunctionType>()) { + blockType->getPointeeType()->getAs<FunctionType>()) { if (ft->getReturnType()->isVoidType()) returnsVoid = true; if (ft->getNoReturnAttr()) @@ -228,14 +224,14 @@ void FallThroughWarningPass::checkFallThroughForFuncBody( // Short circuit for compilation speed. if (cd.checkDiagnostics(diags, returnsVoid, hasNoReturn)) - return; + return; // cpu_dispatch functions permit empty function bodies for ICC compatibility. // TODO: Do we have isCPUDispatchMultiVersion? switch (ControlFlowKind fallThroughType = checkFallThrough(cfg)) { case UnknownFallThrough: - [[fallthrough]]; + break; case MaybeFallThrough: [[fallthrough]]; case AlwaysFallThrough: @@ -267,7 +263,6 @@ void FallThroughWarningPass::checkFallThroughForFuncBody( bool notInAllControlPaths = fallThroughType == MaybeFallThrough; s.Diag(rBrace, cd.diagFallThroughReturnsNonVoid) << cd.funKind << notInAllControlPaths; - } break; case NeverFallThroughOrReturn: @@ -332,7 +327,8 @@ ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { mlir::Operation *term = pred.getTerminator(); - // TODO: hasNoReturnElement() in OG here, not sure how to work it in here yet + // TODO: hasNoReturnElement() in OG here, not sure how to work it in here + // yet // INFO: In OG, we'll be looking for destructor since it can appear past // return but i guess not in CIR? In this case we'll only be examining the @@ -348,7 +344,7 @@ ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { hasPlainEdge = std::distance(pred.begin(), pred.end()) == 1; if (auto returnOp = dyn_cast<cir::ReturnOp>(term)) { - if (!isPhonyReturn(returnOp)) { + if (!isPhonyReturn(returnOp)) { hasLiveReturn = true; continue; } diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 1b06c311447af..627265cec3d3b 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -118,7 +118,8 @@ class CIRGenConsumer : public clang::ASTConsumer { // Run CIR analysis passes if requested if (!FEOptions.ClangIRAnalysisList.empty()) { - CIRAnalysisSet AnalysisSet = parseCIRAnalysisList(FEOptions.ClangIRAnalysisList); + CIRAnalysisSet AnalysisSet = + parseCIRAnalysisList(FEOptions.ClangIRAnalysisList); if (AnalysisSet.has(CIRAnalysisKind::FallThrough)) { if (CI.hasSema()) { @@ -138,12 +139,14 @@ class CIRGenConsumer : public clang::ASTConsumer { const CheckFallThroughDiagnostics &CD = (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::makeForBlock() : (isa<CXXMethodDecl>(D) && - cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call && + cast<CXXMethodDecl>(D)->getOverloadedOperator() == + OO_Call && cast<CXXMethodDecl>(D)->getParent()->isLambda()) ? CheckFallThroughDiagnostics::makeForLambda() - : CheckFallThroughDiagnostics::makeForFunction(S, D)); + : CheckFallThroughDiagnostics::makeForFunction(S, D)); // Run fall-through analysis on this function - FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, CD); + FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, + CD); }); } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
