Files
TheRock/compiler/amd-llvm/clang/lib/Sema/SemaLifetimeSafety.h
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

489 lines
21 KiB
C++

//===--- SemaLifetimeSafety.h - Sema support for lifetime safety =---------==//
//
// 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 file defines the Sema-specific implementation for lifetime safety
// analysis. It provides diagnostic reporting and helper functions that bridge
// the lifetime safety analysis framework with Sema's diagnostic engine.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
#define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/Sema.h"
#include <string>
namespace clang::lifetimes {
inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
if (S.getLangOpts().DebugRunLifetimeSafety)
return true;
DiagnosticsEngine &Diags = S.getDiagnostics();
constexpr unsigned DiagIDs[] = {
diag::warn_lifetime_safety_use_after_scope,
diag::warn_lifetime_safety_use_after_scope_moved,
diag::warn_lifetime_safety_use_after_free,
diag::warn_lifetime_safety_return_stack_addr,
diag::warn_lifetime_safety_return_stack_addr_moved,
diag::warn_lifetime_safety_invalidation,
diag::warn_lifetime_safety_dangling_field,
diag::warn_lifetime_safety_dangling_field_moved,
diag::warn_lifetime_safety_dangling_global,
diag::warn_lifetime_safety_dangling_global_moved,
diag::warn_lifetime_safety_noescape_escapes,
diag::warn_lifetime_safety_lifetimebound_violation,
diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound,
diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound,
diag::warn_lifetime_safety_invalidated_field,
diag::warn_lifetime_safety_invalidated_global,
diag::warn_lifetime_safety_cross_tu_param_suggestion,
diag::warn_lifetime_safety_intra_tu_param_suggestion,
diag::warn_lifetime_safety_cross_tu_this_suggestion,
diag::warn_lifetime_safety_intra_tu_this_suggestion};
for (unsigned DiagID : DiagIDs)
if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
return true;
return false;
}
class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
public:
LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
SourceLocation FreeLoc) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_use_after_scope_moved
: diag::warn_lifetime_safety_use_after_scope;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
}
void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
const Expr *MovedExpr) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_return_stack_addr_moved
: diag::warn_lifetime_safety_return_stack_addr;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
<< ReturnExpr->getSourceRange();
}
void reportDanglingField(const Expr *IssueExpr,
const FieldDecl *DanglingField,
const Expr *MovedExpr,
SourceLocation ExpiryLoc) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_dangling_field_moved
: diag::warn_lifetime_safety_dangling_field;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr)
<< getDiagSubjectDescription(DanglingField)
<< IssueExpr->getSourceRange();
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
}
void reportDanglingGlobal(const Expr *IssueExpr,
const VarDecl *DanglingGlobal,
const Expr *MovedExpr,
SourceLocation ExpiryLoc) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_dangling_global_moved
: diag::warn_lifetime_safety_dangling_global;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr)
<< getDiagSubjectDescription(DanglingGlobal)
<< IssueExpr->getSourceRange();
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
<< DanglingGlobal->getEndLoc();
else
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_global_here)
<< DanglingGlobal->getEndLoc();
}
void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *InvalidationExpr) override {
auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::warn_lifetime_safety_use_after_free
: diag::warn_lifetime_safety_invalidation;
auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(IssueExpr->getExprLoc(), WarnDiag)
<< false << IssueExpr->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
<< InvalidationExpr->getSourceRange();
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
}
void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
const Expr *InvalidationExpr) override {
auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::warn_lifetime_safety_use_after_free
: diag::warn_lifetime_safety_invalidation;
auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
<< true << PVD->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
<< InvalidationExpr->getSourceRange();
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
}
void reportInvalidatedField(const Expr *IssueExpr,
const FieldDecl *DanglingField,
const Expr *InvalidationExpr) override {
auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(IssueExpr->getExprLoc(),
diag::warn_lifetime_safety_invalidated_field)
<< false << IssueExpr->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
<< InvalidationExpr->getSourceRange();
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
}
void reportInvalidatedField(const ParmVarDecl *PVD,
const FieldDecl *DanglingField,
const Expr *InvalidationExpr) override {
auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(PVD->getSourceRange().getBegin(),
diag::warn_lifetime_safety_invalidated_field)
<< true << PVD->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
<< InvalidationExpr->getSourceRange();
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
}
void reportInvalidatedGlobal(const Expr *IssueExpr,
const VarDecl *DanglingGlobal,
const Expr *InvalidationExpr) override {
auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(IssueExpr->getExprLoc(),
diag::warn_lifetime_safety_invalidated_global)
<< false << IssueExpr->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
<< InvalidationExpr->getSourceRange();
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
<< DanglingGlobal->getEndLoc();
else
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_global_here)
<< DanglingGlobal->getEndLoc();
}
void reportInvalidatedGlobal(const ParmVarDecl *PVD,
const VarDecl *DanglingGlobal,
const Expr *InvalidationExpr) override {
auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
? diag::note_lifetime_safety_freed_here
: diag::note_lifetime_safety_invalidated_here;
S.Diag(PVD->getSourceRange().getBegin(),
diag::warn_lifetime_safety_invalidated_global)
<< true << PVD->getSourceRange();
S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
<< InvalidationExpr->getSourceRange();
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
<< DanglingGlobal->getEndLoc();
else
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_global_here)
<< DanglingGlobal->getEndLoc();
}
void suggestLifetimeboundToParmVar(WarningScope Scope,
const ParmVarDecl *ParmToAnnotate,
EscapingTarget Target) override {
unsigned DiagID =
(Scope == WarningScope::CrossTU)
? diag::warn_lifetime_safety_cross_tu_param_suggestion
: diag::warn_lifetime_safety_intra_tu_param_suggestion;
auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(ParmToAnnotate);
S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
<< ParmToAnnotate->getSourceRange()
<< FixItHint::CreateInsertion(InsertionPoint, FixItText);
if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
<< EscapeExpr->getSourceRange();
else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
S.Diag(EscapeField->getLocation(),
diag::note_lifetime_safety_escapes_to_field_here)
<< EscapeField->getSourceRange();
}
void reportLifetimeboundViolation(
const ParmVarDecl *ParmWithLifetimebound) override {
const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
StringRef ParamName = ParmWithLifetimebound->getName();
bool HasName = ParamName.size() > 0;
S.Diag(Attr->getLocation(),
diag::warn_lifetime_safety_lifetimebound_violation)
<< HasName << ParamName << Attr->getRange();
}
void reportLifetimeboundViolation(
const CXXMethodDecl *MDWithLifetimebound) override {
const auto *Attr =
getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound);
assert(Attr && "Expected lifetimebound attribute");
S.Diag(Attr->getLocation(),
diag::warn_lifetime_safety_lifetimebound_violation)
<< 2 << "" << Attr->getRange();
}
void reportMisplacedLifetimebound(WarningScope Scope,
const CXXMethodDecl *FDef,
const CXXMethodDecl *FDecl) override {
const auto *Attr = getDirectImplicitObjectLifetimeBoundAttr(FDef);
assert(Attr && "Expected lifetimebound attribute");
unsigned DiagID =
Scope == WarningScope::CrossTU
? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
: diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(FDecl);
// Do not emit fix-its in macros or at invalid locations.
bool IsMacro =
FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
if (IsMacro || InsertionPoint.isInvalid())
S.Diag(FDecl->getLocation(), DiagID);
else
S.Diag(InsertionPoint, DiagID)
<< FixItHint::CreateInsertion(InsertionPoint, FixItText);
S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
<< Attr->getRange();
}
void reportMisplacedLifetimebound(WarningScope Scope,
const ParmVarDecl *PVDDef,
const ParmVarDecl *PVDDecl) override {
const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
assert(Attr && "Expected lifetimebound attribute");
unsigned DiagID =
Scope == WarningScope::CrossTU
? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
: diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(PVDDecl);
// Do not emit fix-its in macros or at invalid locations.
bool IsMacro =
PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
if (IsMacro || InsertionPoint.isInvalid())
S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
else
S.Diag(PVDDecl->getBeginLoc(), DiagID)
<< PVDDecl->getSourceRange()
<< FixItHint::CreateInsertion(InsertionPoint, FixItText);
S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
<< Attr->getRange();
}
void suggestLifetimeboundToImplicitThis(WarningScope Scope,
const CXXMethodDecl *MD,
const Expr *EscapeExpr) override {
unsigned DiagID = (Scope == WarningScope::CrossTU)
? diag::warn_lifetime_safety_cross_tu_this_suggestion
: diag::warn_lifetime_safety_intra_tu_this_suggestion;
auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
S.Diag(InsertionPoint, DiagID)
<< MD->getNameInfo().getSourceRange()
<< FixItHint::CreateInsertion(InsertionPoint, FixItText);
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
<< EscapeExpr->getSourceRange();
}
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const Expr *EscapeExpr) override {
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
<< ParmWithNoescape->getSourceRange();
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
<< EscapeExpr->getSourceRange();
}
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const FieldDecl *EscapeField) override {
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
<< ParmWithNoescape->getSourceRange();
S.Diag(EscapeField->getLocation(),
diag::note_lifetime_safety_escapes_to_field_here)
<< EscapeField->getEndLoc();
}
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const VarDecl *EscapeGlobal) override {
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
<< ParmWithNoescape->getSourceRange();
if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
S.Diag(EscapeGlobal->getLocation(),
diag::note_lifetime_safety_escapes_to_static_storage_here)
<< EscapeGlobal->getEndLoc();
else
S.Diag(EscapeGlobal->getLocation(),
diag::note_lifetime_safety_escapes_to_global_here)
<< EscapeGlobal->getEndLoc();
}
void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override {
S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
}
private:
std::pair<SourceLocation, StringRef>
getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
Decl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
StringRef FixItText = " [[clang::lifetimebound]]";
if (!Decl->getIdentifier()) {
// For unnamed parameters, placing attributes after the type would be
// parsed as a type attribute, not a parameter attribute.
InsertionPoint = Decl->getBeginLoc();
FixItText = "[[clang::lifetimebound]] ";
} else if (Decl->hasDefaultArg()) {
// If the parameter has a default argument, place the attribute after the
// named argument.
InsertionPoint = Lexer::getLocForEndOfToken(
Decl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
}
return {InsertionPoint, FixItText};
}
std::pair<SourceLocation, StringRef>
getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
FPT && FPT->hasTrailingReturn()) {
// For trailing return types, 'getEndLoc()' includes the return type
// after '->', placing the attribute in an invalid position.
// Instead use 'getLocalRangeEnd()' which gives the '->' location
// for trailing returns, so find the last token before it.
const auto FTL = MDL.getAs<FunctionTypeLoc>();
assert(FTL);
InsertionPoint = Lexer::getLocForEndOfToken(
Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
S.getLangOpts(),
/*IncludeComments=*/false)
->getLocation(),
0, S.getSourceManager(), S.getLangOpts());
}
return {InsertionPoint, " [[clang::lifetimebound]]"};
}
std::string getDiagSubjectDescription(const ValueDecl *VD) {
std::string Res;
llvm::raw_string_ostream OS(Res);
if (isa<FieldDecl>(VD)) {
OS << "field";
} else if (isa<ParmVarDecl>(VD)) {
OS << "parameter";
} else if (const auto *Var = dyn_cast<VarDecl>(VD)) {
if (Var->isStaticLocal() || Var->isStaticDataMember())
OS << "static variable";
else if (Var->hasGlobalStorage())
OS << "global variable";
else
OS << "local variable";
} else {
OS << "variable";
}
OS << " '";
VD->getNameForDiagnostic(OS, S.getPrintingPolicy(), /*Qualified=*/false);
OS << "'";
return Res;
}
std::string getDiagSubjectDescription(const Expr *E) {
if (isa<MaterializeTemporaryExpr>(E))
return "local temporary object";
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
return getDiagSubjectDescription(DRE->getDecl());
// TODO: Handle other expression types.
return "";
}
Sema &S;
};
} // namespace clang::lifetimes
#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H