summaryrefslogtreecommitdiff
path: root/libcody/internal.hh
blob: 8901629cddbf7078e8f8fd86656328dd3357bbc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// CODYlib		-*- mode:c++ -*-
// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
// License: Apache v2.0

#include "cody.hh"

#ifndef __has_builtin
#define __has_builtin(X) 0
#endif
#ifndef __has_include
#define __has_include(X) 0
#endif

// C++
#if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE)
#define CODY_LOC_BUILTIN 1
#elif __has_include (<source_location>)
#include <source_location>
#ifdef __cpp_lib_source_location
#define CODY_LOC_SOURCE 1
#endif
#endif

// C
#include <cstdio>

namespace Cody {

// Location is needed regardless of checking, to make the fatal
// handler simpler
class Location
{
protected:
  char const *file;
  unsigned line;

public:
  constexpr Location (char const *file_
#if CODY_LOC_BUILTIN
		      = __builtin_FILE ()
#elif !CODY_LOC_SOURCE
		      = nullptr
#endif
		      , unsigned line_
#if CODY_LOC_BUILTIN
		      = __builtin_LINE ()
#elif !CODY_LOC_SOURCE
		      = 0
#endif
		      )
    :file (file_), line (line_)
  {
  }

#if !CODY_LOC_BUILTIN && CODY_LOC_SOURCE
  using source_location = std::source_location;

  constexpr Location (source_location loc = source_location::current ())
    : Location (loc.file (), loc.line ())
  {
  }
#endif

public:
  constexpr char const *File () const
  {
    return file;
  }
  constexpr unsigned Line () const
  {
    return line;
  }
};

void HCF [[noreturn]]
(
 char const *msg
#if NMS_CHECKING
 , Location const = Location ()
#if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE
#define HCF(M) HCF ((M), Cody::Location (__FILE__, __LINE__))
#endif
#endif
 ) noexcept;

#if NMS_CHECKING
void AssertFailed [[noreturn]] (Location loc = Location ()) noexcept;
void Unreachable [[noreturn]] (Location loc = Location ()) noexcept;
#if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE
#define AssertFailed() AssertFailed (Cody::Location (__FILE__, __LINE__))
#define Unreachable() Unreachable (Cody::Location (__FILE__, __LINE__))
#endif

// Do we have __VA_OPT__, alas no specific feature macro for it :(
// From stack overflow
// https://stackoverflow.com/questions/48045470/portably-detect-va-opt-support
// Relies on having variadic macros, but they're a C++11 thing, so
// we're good
#define HAVE_ARG_3(a,b,c,...) c
#define HAVE_VA_OPT_(...) HAVE_ARG_3(__VA_OPT__(,),true,false,)
#define HAVE_VA_OPT HAVE_VA_OPT_(?)

// Oh, for lazily evaluated function parameters
#if HAVE_VA_OPT
// Assert is variadic, so you can write Assert (TPL<A,B>(C)) without
// extraneous parens.  I don't think we need that though.
#define Assert(EXPR, ...)						\
  (__builtin_expect (bool (EXPR __VA_OPT__ (, __VA_ARGS__)), true)	\
   ? (void)0 : AssertFailed ())
#else
// If you don't have the GNU ,##__VA_ARGS__ pasting extension, we'll
// need another fallback
#define Assert(EXPR, ...)						\
  (__builtin_expect (bool (EXPR, ##__VA_ARGS__), true)			\
   ? (void)0 : AssertFailed ())
#endif
#else
// Not asserting, use EXPR in an unevaluated context
#if  HAVE_VA_OPT
#define Assert(EXPR, ...)					\
  ((void)sizeof (bool (EXPR __VA_OPT__ (, __VA_ARGS__))), (void)0)
#else
#define Assert(EXPR, ...)					\
  ((void)sizeof (bool (EXPR, ##__VA_ARGS__)), (void)0)
#endif

inline void Unreachable () noexcept
{
  __builtin_unreachable ();
}
#endif

}