From c51f86cd13bff130db14a767417d100b4b5434c1 Mon Sep 17 00:00:00 2001
From: scott snyder <snyder@bnl.gov>
Date: Tue, 19 Jan 2021 19:58:46 +0100
Subject: [PATCH] AthenaKernel: Fix Units for clang.

The Units header provides units objects for which division is automatically changed at compile-time
to multiplication by a reciprocal of the unit constant.  This relied on the compiler changing
(1./x) to multiplication by a reciprocal, if x is constant.  However, this does not work
with clang if the -ffp-exception-behavior=maytrap option is used.

Rewrite to fix this.  The price is that we need to have a separate class for each unit
(which can then return the value as a constexpr).
---
 Control/AthenaKernel/AthenaKernel/Units.h | 52 +++++++++++------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/Control/AthenaKernel/AthenaKernel/Units.h b/Control/AthenaKernel/AthenaKernel/Units.h
index 264a2f7a7069..72c0f9625403 100644
--- a/Control/AthenaKernel/AthenaKernel/Units.h
+++ b/Control/AthenaKernel/AthenaKernel/Units.h
@@ -1,10 +1,9 @@
 // This file's extension implies that it's C, but it's really -*- C++ -*-.
 
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
-// $Id$
 /**
  * @file AthenaKernel/Units.h
  * @author scott snyder <snyder@bnl.gov>
@@ -43,42 +42,43 @@
 
 
 namespace Athena {
+namespace Units {
 
 
 /**
  * @brief Wrapper to avoid constant divisions when using units.
+ *        The Unit class defined here is a placeholder.
+ *        A specialization will be generated for each unit by the UNIT
+ *        macro below.  TAG is used to distinguish these specializations.
+ *
+ *        (We used to do this with a single, non-template, class,
+ *        but that didn't work with clang when -ffp-exception-behavior=maytrap
+ *        is used.)
  */
-class Unit
-{
-public:
-  constexpr Unit(double val) : m_val(val) {}
-  constexpr operator double() const { return m_val; }
-
-private:
-  double m_val;
-};
-
-
-constexpr double operator/ (double x, const Unit& u)
+template <int TAG> class Unit {};
+template <int TAG>
+constexpr double operator/ (double x, const Unit<TAG> u)
 {
-  return x * (1./static_cast<double>(u));
+  constexpr double recip = 1. / static_cast<double>(u);
+  return x * recip;
 }
-
-
-constexpr float operator/ (float x, const Unit& u)
+template <int TAG>
+constexpr float operator/ (float x, const Unit<TAG> u)
 {
-  return x * (1./static_cast<float>(u));
+  constexpr float recip = 1. / static_cast<float>(u);
+  return x * recip;
 }
 
 
-namespace Units {
-
-
-// Would be nice to declare this constexpr, but can't in general
-// because the symbols from SystemOfUnits aren't constexpr.
-//
 // Include a selection of units from SystemOfUnits.h
-#define UNIT(NAME) /*constexpr*/ static const Unit NAME (Gaudi::Units::NAME)
+#define UNIT(NAME)                                                      \
+template <> class Unit<__LINE__>                                        \
+{                                                                       \
+public:                                                                 \
+  constexpr operator double() const { return Gaudi::Units::NAME; }      \
+};                                                                      \
+constexpr static const Unit<__LINE__> NAME
+
 UNIT (millimeter);
 UNIT (millimeter2);
 UNIT (millimeter3);
-- 
GitLab