Path: blob/main/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
213799 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This contains code to emit Decl nodes as CIR code.9//10//===----------------------------------------------------------------------===//1112#include "CIRGenConstantEmitter.h"13#include "CIRGenFunction.h"14#include "mlir/IR/Location.h"15#include "clang/AST/Attr.h"16#include "clang/AST/Decl.h"17#include "clang/AST/DeclOpenACC.h"18#include "clang/AST/Expr.h"19#include "clang/AST/ExprCXX.h"20#include "clang/CIR/MissingFeatures.h"2122using namespace clang;23using namespace clang::CIRGen;2425CIRGenFunction::AutoVarEmission26CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {27QualType ty = d.getType();28if (ty.getAddressSpace() != LangAS::Default)29cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space");3031mlir::Location loc = getLoc(d.getSourceRange());3233CIRGenFunction::AutoVarEmission emission(d);34emission.IsEscapingByRef = d.isEscapingByref();35if (emission.IsEscapingByRef)36cgm.errorNYI(d.getSourceRange(),37"emitAutoVarDecl: decl escaping by reference");3839CharUnits alignment = getContext().getDeclAlign(&d);4041// If the type is variably-modified, emit all the VLA sizes for it.42if (ty->isVariablyModifiedType())43cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: variably modified type");4445Address address = Address::invalid();46if (!ty->isConstantSizeType())47cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: non-constant size type");4849// A normal fixed sized variable becomes an alloca in the entry block,50mlir::Type allocaTy = convertTypeForMem(ty);51// Create the temp alloca and declare variable using it.52address = createTempAlloca(allocaTy, alignment, loc, d.getName());53declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);5455emission.Addr = address;56setAddrOfLocalVar(&d, address);5758return emission;59}6061/// Determine whether the given initializer is trivial in the sense62/// that it requires no code to be generated.63bool CIRGenFunction::isTrivialInitializer(const Expr *init) {64if (!init)65return true;6667if (const CXXConstructExpr *construct = dyn_cast<CXXConstructExpr>(init))68if (CXXConstructorDecl *constructor = construct->getConstructor())69if (constructor->isTrivial() && constructor->isDefaultConstructor() &&70!construct->requiresZeroInitialization())71return true;7273return false;74}7576void CIRGenFunction::emitAutoVarInit(77const CIRGenFunction::AutoVarEmission &emission) {78assert(emission.Variable && "emission was not valid!");7980// If this was emitted as a global constant, we're done.81if (emission.wasEmittedAsGlobal())82return;8384const VarDecl &d = *emission.Variable;8586QualType type = d.getType();8788// If this local has an initializer, emit it now.89const Expr *init = d.getInit();9091// Initialize the variable here if it doesn't have a initializer and it is a92// C struct that is non-trivial to initialize or an array containing such a93// struct.94if (!init && type.isNonTrivialToPrimitiveDefaultInitialize() ==95QualType::PDIK_Struct) {96cgm.errorNYI(d.getSourceRange(),97"emitAutoVarInit: non-trivial to default initialize");98return;99}100101const Address addr = emission.Addr;102103// Check whether this is a byref variable that's potentially104// captured and moved by its own initializer. If so, we'll need to105// emit the initializer first, then copy into the variable.106assert(!cir::MissingFeatures::opAllocaCaptureByInit());107108// Note: constexpr already initializes everything correctly.109LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =110(d.isConstexpr()111? LangOptions::TrivialAutoVarInitKind::Uninitialized112: (d.getAttr<UninitializedAttr>()113? LangOptions::TrivialAutoVarInitKind::Uninitialized114: getContext().getLangOpts().getTrivialAutoVarInit()));115116auto initializeWhatIsTechnicallyUninitialized = [&](Address addr) {117if (trivialAutoVarInit ==118LangOptions::TrivialAutoVarInitKind::Uninitialized)119return;120121cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: trivial initialization");122};123124if (isTrivialInitializer(init)) {125initializeWhatIsTechnicallyUninitialized(addr);126return;127}128129mlir::Attribute constant;130if (emission.IsConstantAggregate ||131d.mightBeUsableInConstantExpressions(getContext())) {132// FIXME: Differently from LLVM we try not to emit / lower too much133// here for CIR since we are interested in seeing the ctor in some134// analysis later on. So CIR's implementation of ConstantEmitter will135// frequently return an empty Attribute, to signal we want to codegen136// some trivial ctor calls and whatnots.137constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(d);138if (constant && !mlir::isa<cir::ZeroAttr>(constant) &&139(trivialAutoVarInit !=140LangOptions::TrivialAutoVarInitKind::Uninitialized)) {141cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: constant aggregate");142return;143}144}145146// NOTE(cir): In case we have a constant initializer, we can just emit a147// store. But, in CIR, we wish to retain any ctor calls, so if it is a148// CXX temporary object creation, we ensure the ctor call is used deferring149// its removal/optimization to the CIR lowering.150if (!constant || isa<CXXTemporaryObjectExpr>(init)) {151initializeWhatIsTechnicallyUninitialized(addr);152LValue lv = makeAddrLValue(addr, type, AlignmentSource::Decl);153emitExprAsInit(init, &d, lv);154// In case lv has uses it means we indeed initialized something155// out of it while trying to build the expression, mark it as such.156mlir::Value val = lv.getAddress().getPointer();157assert(val && "Should have an address");158auto allocaOp = dyn_cast_or_null<cir::AllocaOp>(val.getDefiningOp());159assert(allocaOp && "Address should come straight out of the alloca");160161if (!allocaOp.use_empty())162allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));163return;164}165166// FIXME(cir): migrate most of this file to use mlir::TypedAttr directly.167auto typedConstant = mlir::dyn_cast<mlir::TypedAttr>(constant);168assert(typedConstant && "expected typed attribute");169if (!emission.IsConstantAggregate) {170// For simple scalar/complex initialization, store the value directly.171LValue lv = makeAddrLValue(addr, type);172assert(init && "expected initializer");173mlir::Location initLoc = getLoc(init->getSourceRange());174// lv.setNonGC(true);175return emitStoreThroughLValue(176RValue::get(builder.getConstant(initLoc, typedConstant)), lv);177}178}179180void CIRGenFunction::emitAutoVarCleanups(181const CIRGenFunction::AutoVarEmission &emission) {182const VarDecl &d = *emission.Variable;183184// Check the type for a cleanup.185if (d.needsDestruction(getContext()))186cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup");187188assert(!cir::MissingFeatures::opAllocaPreciseLifetime());189190// Handle the cleanup attribute.191if (d.hasAttr<CleanupAttr>())192cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: CleanupAttr");193}194195/// Emit code and set up symbol table for a variable declaration with auto,196/// register, or no storage class specifier. These turn into simple stack197/// objects, globals depending on target.198void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) {199CIRGenFunction::AutoVarEmission emission = emitAutoVarAlloca(d);200emitAutoVarInit(emission);201emitAutoVarCleanups(emission);202}203204void CIRGenFunction::emitVarDecl(const VarDecl &d) {205// If the declaration has external storage, don't emit it now, allow it to be206// emitted lazily on its first use.207if (d.hasExternalStorage())208return;209210if (d.getStorageDuration() != SD_Automatic) {211// Static sampler variables translated to function calls.212if (d.getType()->isSamplerT()) {213// Nothing needs to be done here, but let's flag it as an error until we214// have a test. It requires OpenCL support.215cgm.errorNYI(d.getSourceRange(), "emitVarDecl static sampler type");216return;217}218219cir::GlobalLinkageKind linkage =220cgm.getCIRLinkageVarDefinition(&d, /*IsConstant=*/false);221222// FIXME: We need to force the emission/use of a guard variable for223// some variables even if we can constant-evaluate them because224// we can't guarantee every translation unit will constant-evaluate them.225226return emitStaticVarDecl(d, linkage);227}228229if (d.getType().getAddressSpace() == LangAS::opencl_local)230cgm.errorNYI(d.getSourceRange(), "emitVarDecl openCL address space");231232assert(d.hasLocalStorage());233234CIRGenFunction::VarDeclContext varDeclCtx{*this, &d};235return emitAutoVarDecl(d);236}237238static std::string getStaticDeclName(CIRGenModule &cgm, const VarDecl &d) {239if (cgm.getLangOpts().CPlusPlus)240return cgm.getMangledName(&d).str();241242// If this isn't C++, we don't need a mangled name, just a pretty one.243assert(!d.isExternallyVisible() && "name shouldn't matter");244std::string contextName;245const DeclContext *dc = d.getDeclContext();246if (auto *cd = dyn_cast<CapturedDecl>(dc))247dc = cast<DeclContext>(cd->getNonClosureContext());248if (const auto *fd = dyn_cast<FunctionDecl>(dc))249contextName = std::string(cgm.getMangledName(fd));250else if (isa<BlockDecl>(dc))251cgm.errorNYI(d.getSourceRange(), "block decl context for static var");252else if (isa<ObjCMethodDecl>(dc))253cgm.errorNYI(d.getSourceRange(), "ObjC decl context for static var");254else255cgm.errorNYI(d.getSourceRange(), "Unknown context for static var decl");256257contextName += "." + d.getNameAsString();258return contextName;259}260261// TODO(cir): LLVM uses a Constant base class. Maybe CIR could leverage an262// interface for all constants?263cir::GlobalOp264CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &d,265cir::GlobalLinkageKind linkage) {266// In general, we don't always emit static var decls once before we reference267// them. It is possible to reference them before emitting the function that268// contains them, and it is possible to emit the containing function multiple269// times.270if (cir::GlobalOp existingGV = getStaticLocalDeclAddress(&d))271return existingGV;272273QualType ty = d.getType();274assert(ty->isConstantSizeType() && "VLAs can't be static");275276// Use the label if the variable is renamed with the asm-label extension.277if (d.hasAttr<AsmLabelAttr>())278errorNYI(d.getSourceRange(), "getOrCreateStaticVarDecl: asm label");279280std::string name = getStaticDeclName(*this, d);281282mlir::Type lty = getTypes().convertTypeForMem(ty);283assert(!cir::MissingFeatures::addressSpace());284285if (d.hasAttr<LoaderUninitializedAttr>() || d.hasAttr<CUDASharedAttr>())286errorNYI(d.getSourceRange(),287"getOrCreateStaticVarDecl: LoaderUninitializedAttr");288assert(!cir::MissingFeatures::addressSpace());289290mlir::Attribute init = builder.getZeroInitAttr(convertType(ty));291292cir::GlobalOp gv = builder.createVersionedGlobal(293getModule(), getLoc(d.getLocation()), name, lty, linkage);294// TODO(cir): infer visibility from linkage in global op builder.295gv.setVisibility(getMLIRVisibilityFromCIRLinkage(linkage));296gv.setInitialValueAttr(init);297gv.setAlignment(getASTContext().getDeclAlign(&d).getAsAlign().value());298299if (supportsCOMDAT() && gv.isWeakForLinker())300gv.setComdat(true);301302assert(!cir::MissingFeatures::opGlobalThreadLocal());303304setGVProperties(gv, &d);305306// OG checks if the expected address space, denoted by the type, is the307// same as the actual address space indicated by attributes. If they aren't308// the same, an addrspacecast is emitted when this variable is accessed.309// In CIR however, cir.get_global already carries that information in310// !cir.ptr type - if this global is in OpenCL local address space, then its311// type would be !cir.ptr<..., addrspace(offload_local)>. Therefore we don't312// need an explicit address space cast in CIR: they will get emitted when313// lowering to LLVM IR.314315// Ensure that the static local gets initialized by making sure the parent316// function gets emitted eventually.317const Decl *dc = cast<Decl>(d.getDeclContext());318319// We can't name blocks or captured statements directly, so try to emit their320// parents.321if (isa<BlockDecl>(dc) || isa<CapturedDecl>(dc)) {322dc = dc->getNonClosureContext();323// FIXME: Ensure that global blocks get emitted.324if (!dc)325errorNYI(d.getSourceRange(), "non-closure context");326}327328GlobalDecl gd;329if (isa<CXXConstructorDecl>(dc))330errorNYI(d.getSourceRange(), "C++ constructors static var context");331else if (isa<CXXDestructorDecl>(dc))332errorNYI(d.getSourceRange(), "C++ destructors static var context");333else if (const auto *fd = dyn_cast<FunctionDecl>(dc))334gd = GlobalDecl(fd);335else {336// Don't do anything for Obj-C method decls or global closures. We should337// never defer them.338assert(isa<ObjCMethodDecl>(dc) && "unexpected parent code decl");339}340if (gd.getDecl() && cir::MissingFeatures::openMP()) {341// Disable emission of the parent function for the OpenMP device codegen.342errorNYI(d.getSourceRange(), "OpenMP");343}344345return gv;346}347348/// Add the initializer for 'd' to the global variable that has already been349/// created for it. If the initializer has a different type than gv does, this350/// may free gv and return a different one. Otherwise it just returns gv.351cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(352const VarDecl &d, cir::GlobalOp gv, cir::GetGlobalOp gvAddr) {353ConstantEmitter emitter(*this);354mlir::TypedAttr init =355mlir::cast<mlir::TypedAttr>(emitter.tryEmitForInitializer(d));356357// If constant emission failed, then this should be a C++ static358// initializer.359if (!init) {360cgm.errorNYI(d.getSourceRange(), "static var without initializer");361return gv;362}363364// TODO(cir): There should be debug code here to assert that the decl size365// matches the CIR data layout type alloc size, but the code for calculating366// the type alloc size is not implemented yet.367assert(!cir::MissingFeatures::dataLayoutTypeAllocSize());368369// The initializer may differ in type from the global. Rewrite370// the global to match the initializer. (We have to do this371// because some types, like unions, can't be completely represented372// in the LLVM type system.)373if (gv.getSymType() != init.getType()) {374gv.setSymType(init.getType());375376// Normally this should be done with a call to cgm.replaceGlobal(oldGV, gv),377// but since at this point the current block hasn't been really attached,378// there's no visibility into the GetGlobalOp corresponding to this Global.379// Given those constraints, thread in the GetGlobalOp and update it380// directly.381assert(!cir::MissingFeatures::addressSpace());382gvAddr.getAddr().setType(builder.getPointerTo(init.getType()));383}384385bool needsDtor =386d.needsDestruction(getContext()) == QualType::DK_cxx_destructor;387388assert(!cir::MissingFeatures::opGlobalConstant());389gv.setInitialValueAttr(init);390391emitter.finalize(gv);392393if (needsDtor) {394// We have a constant initializer, but a nontrivial destructor. We still395// need to perform a guarded "initialization" in order to register the396// destructor.397cgm.errorNYI(d.getSourceRange(), "C++ guarded init");398}399400return gv;401}402403void CIRGenFunction::emitStaticVarDecl(const VarDecl &d,404cir::GlobalLinkageKind linkage) {405// Check to see if we already have a global variable for this406// declaration. This can happen when double-emitting function407// bodies, e.g. with complete and base constructors.408cir::GlobalOp globalOp = cgm.getOrCreateStaticVarDecl(d, linkage);409// TODO(cir): we should have a way to represent global ops as values without410// having to emit a get global op. Sometimes these emissions are not used.411mlir::Value addr = builder.createGetGlobal(globalOp);412auto getAddrOp = mlir::cast<cir::GetGlobalOp>(addr.getDefiningOp());413414CharUnits alignment = getContext().getDeclAlign(&d);415416// Store into LocalDeclMap before generating initializer to handle417// circular references.418mlir::Type elemTy = convertTypeForMem(d.getType());419setAddrOfLocalVar(&d, Address(addr, elemTy, alignment));420421// We can't have a VLA here, but we can have a pointer to a VLA,422// even though that doesn't really make any sense.423// Make sure to evaluate VLA bounds now so that we have them for later.424if (d.getType()->isVariablyModifiedType()) {425cgm.errorNYI(d.getSourceRange(),426"emitStaticVarDecl: variably modified type");427}428429// Save the type in case adding the initializer forces a type change.430mlir::Type expectedType = addr.getType();431432cir::GlobalOp var = globalOp;433434assert(!cir::MissingFeatures::cudaSupport());435436// If this value has an initializer, emit it.437if (d.getInit())438var = addInitializerToStaticVarDecl(d, var, getAddrOp);439440var.setAlignment(alignment.getAsAlign().value());441442// There are a lot of attributes that need to be handled here. Until443// we start to support them, we just report an error if there are any.444if (d.hasAttrs())445cgm.errorNYI(d.getSourceRange(), "static var with attrs");446447if (cgm.getCodeGenOpts().KeepPersistentStorageVariables)448cgm.errorNYI(d.getSourceRange(), "static var keep persistent storage");449450// From traditional codegen:451// We may have to cast the constant because of the initializer452// mismatch above.453//454// FIXME: It is really dangerous to store this in the map; if anyone455// RAUW's the GV uses of this constant will be invalid.456mlir::Value castedAddr =457builder.createBitcast(getAddrOp.getAddr(), expectedType);458localDeclMap.find(&d)->second = Address(castedAddr, elemTy, alignment);459cgm.setStaticLocalDeclAddress(&d, var);460461assert(!cir::MissingFeatures::sanitizers());462assert(!cir::MissingFeatures::generateDebugInfo());463}464465void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,466LValue lvalue, bool capturedByInit) {467assert(!cir::MissingFeatures::objCLifetime());468469SourceLocRAIIObject locRAII{*this, loc};470mlir::Value value = emitScalarExpr(init);471if (capturedByInit) {472cgm.errorNYI(init->getSourceRange(), "emitScalarInit: captured by init");473return;474}475assert(!cir::MissingFeatures::emitNullabilityCheck());476emitStoreThroughLValue(RValue::get(value), lvalue, true);477}478479void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,480LValue lvalue, bool capturedByInit) {481SourceLocRAIIObject loc{*this, getLoc(init->getSourceRange())};482if (capturedByInit) {483cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: captured by init");484return;485}486487QualType type = d->getType();488489if (type->isReferenceType()) {490RValue rvalue = emitReferenceBindingToExpr(init);491if (capturedByInit)492cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: captured by init");493emitStoreThroughLValue(rvalue, lvalue);494return;495}496switch (CIRGenFunction::getEvaluationKind(type)) {497case cir::TEK_Scalar:498emitScalarInit(init, getLoc(d->getSourceRange()), lvalue);499return;500case cir::TEK_Complex: {501mlir::Value complex = emitComplexExpr(init);502if (capturedByInit)503cgm.errorNYI(init->getSourceRange(),504"emitExprAsInit: complex type captured by init");505mlir::Location loc = getLoc(init->getExprLoc());506emitStoreOfComplex(loc, complex, lvalue,507/*isInit*/ true);508return;509}510case cir::TEK_Aggregate:511// The overlap flag here should be calculated.512assert(!cir::MissingFeatures::aggValueSlotMayOverlap());513emitAggExpr(init,514AggValueSlot::forLValue(lvalue, AggValueSlot::IsDestructed,515AggValueSlot::IsNotAliased,516AggValueSlot::MayOverlap));517return;518}519llvm_unreachable("bad evaluation kind");520}521522void CIRGenFunction::emitDecl(const Decl &d) {523switch (d.getKind()) {524case Decl::BuiltinTemplate:525case Decl::TranslationUnit:526case Decl::ExternCContext:527case Decl::Namespace:528case Decl::UnresolvedUsingTypename:529case Decl::ClassTemplateSpecialization:530case Decl::ClassTemplatePartialSpecialization:531case Decl::VarTemplateSpecialization:532case Decl::VarTemplatePartialSpecialization:533case Decl::TemplateTypeParm:534case Decl::UnresolvedUsingValue:535case Decl::NonTypeTemplateParm:536case Decl::CXXDeductionGuide:537case Decl::CXXMethod:538case Decl::CXXConstructor:539case Decl::CXXDestructor:540case Decl::CXXConversion:541case Decl::Field:542case Decl::MSProperty:543case Decl::IndirectField:544case Decl::ObjCIvar:545case Decl::ObjCAtDefsField:546case Decl::ParmVar:547case Decl::ImplicitParam:548case Decl::ClassTemplate:549case Decl::VarTemplate:550case Decl::FunctionTemplate:551case Decl::TypeAliasTemplate:552case Decl::TemplateTemplateParm:553case Decl::ObjCMethod:554case Decl::ObjCCategory:555case Decl::ObjCProtocol:556case Decl::ObjCInterface:557case Decl::ObjCCategoryImpl:558case Decl::ObjCImplementation:559case Decl::ObjCProperty:560case Decl::ObjCCompatibleAlias:561case Decl::PragmaComment:562case Decl::PragmaDetectMismatch:563case Decl::AccessSpec:564case Decl::LinkageSpec:565case Decl::Export:566case Decl::ObjCPropertyImpl:567case Decl::FileScopeAsm:568case Decl::Friend:569case Decl::FriendTemplate:570case Decl::Block:571case Decl::OutlinedFunction:572case Decl::Captured:573case Decl::UsingShadow:574case Decl::ConstructorUsingShadow:575case Decl::ObjCTypeParam:576case Decl::Binding:577case Decl::UnresolvedUsingIfExists:578case Decl::HLSLBuffer:579case Decl::HLSLRootSignature:580llvm_unreachable("Declaration should not be in declstmts!");581582case Decl::Function: // void X();583case Decl::EnumConstant: // enum ? { X = ? }584case Decl::StaticAssert: // static_assert(X, ""); [C++0x]585case Decl::Label: // __label__ x;586case Decl::Import:587case Decl::MSGuid: // __declspec(uuid("..."))588case Decl::TemplateParamObject:589case Decl::OMPThreadPrivate:590case Decl::OMPAllocate:591case Decl::OMPCapturedExpr:592case Decl::OMPRequires:593case Decl::Empty:594case Decl::Concept:595case Decl::LifetimeExtendedTemporary:596case Decl::RequiresExprBody:597case Decl::UnnamedGlobalConstant:598// None of these decls require codegen support.599return;600601case Decl::Enum: // enum X;602case Decl::Record: // struct/union/class X;603case Decl::CXXRecord: // struct/union/class X; [C++]604case Decl::NamespaceAlias:605case Decl::Using: // using X; [C++]606case Decl::UsingEnum: // using enum X; [C++]607case Decl::UsingDirective: // using namespace X; [C++]608assert(!cir::MissingFeatures::generateDebugInfo());609return;610case Decl::Var: {611const VarDecl &vd = cast<VarDecl>(d);612assert(vd.isLocalVarDecl() &&613"Should not see file-scope variables inside a function!");614emitVarDecl(vd);615return;616}617case Decl::OpenACCDeclare:618emitOpenACCDeclare(cast<OpenACCDeclareDecl>(d));619return;620case Decl::OpenACCRoutine:621emitOpenACCRoutine(cast<OpenACCRoutineDecl>(d));622return;623case Decl::Typedef: // typedef int X;624case Decl::TypeAlias: { // using X = int; [C++0x]625QualType ty = cast<TypedefNameDecl>(d).getUnderlyingType();626assert(!cir::MissingFeatures::generateDebugInfo());627if (ty->isVariablyModifiedType())628cgm.errorNYI(d.getSourceRange(), "emitDecl: variably modified type");629return;630}631case Decl::ImplicitConceptSpecialization:632case Decl::TopLevelStmt:633case Decl::UsingPack:634case Decl::Decomposition: // This could be moved to join Decl::Var635case Decl::OMPDeclareReduction:636case Decl::OMPDeclareMapper:637cgm.errorNYI(d.getSourceRange(),638std::string("emitDecl: unhandled decl type: ") +639d.getDeclKindName());640}641}642643void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,644SourceLocation loc) {645if (!sanOpts.has(SanitizerKind::NullabilityAssign))646return;647648assert(!cir::MissingFeatures::sanitizers());649}650651652