forked from apple/swift
/
TypeLookupError.h
199 lines (166 loc) · 6.74 KB
/
TypeLookupError.h
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//===--- TypeLookupError.h - Type lookup error value. -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Provides the TypeLookupError class, which represents errors when demangling
// or looking up types.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_DEMANGLING_TypeLookupError_H
#define SWIFT_DEMANGLING_TypeLookupError_H
#include "swift/Basic/TaggedUnion.h"
#include "swift/Runtime/Portability.h"
namespace swift {
/// An error that occurred while looking up a type at runtime from a mangled
/// name.
/// ///
/// This ultimately just provides a string, but is built to take up minimal
/// space when passed around, and perform as much work lazily as possible. We
/// don't want to spend a lot of time building strings when the caller is going
/// to handle the error gracefully and try a fallback. We only want to waste
/// time/space on that when the error message is actually relevant, so build it
/// as late as possible.
/// ///
/// To be as compact as possible, this type holds a context pointer and a
/// callback function. The callback function uses the context pointer to return
/// an error string when requested. The callback function also does quadruple
/// duty to copy/destroy the context as needed, and free the returned error
/// string if needed. Commands are passed to the callback to request the
/// different operations, which means we only have to store one function pointer
/// instead of four.
class TypeLookupError {
public:
/// The commands that can be passed to the callback function.
enum class Command {
/// Return the error string to the caller, as a char *.
CopyErrorString,
/// Destroy the error string returned from CopyErrorString, if necessary.
/// The return value is ignored.
DestroyErrorString,
/// Return a copy of the context pointer (used for copying TypeLookupError
/// objects.)
CopyContext,
/// Destroy the context pointer. The return value is ignored.
DestroyContext,
};
/// The callback used to respond to the commands. The parameters are:
/// - `context`: the context that the value was initialized with, or the
/// context returned from a CopyContext call
/// - `command`: the command to respond to
/// - `param`: when `command` is `DestroyErrorString`, the string pointer to
/// destroy, otherwise NULL
using Callback = void *(*)(void *context, Command command, void *param);
private:
void *Context;
Callback Fn;
/// A no-op callback used to avoid a bunch of `if (Fn)` checks.
static void *nop(void *context, Command command, void *param) {
return nullptr;
}
/// Helper functions for getting a C string from a lambda. These allow us to
/// wrap lambdas returning `char *` or `std::string` and standardize them on
/// `char *`.
static char *getCString(char *str) { return str; }
static char *getCString(const std::string &str) {
return strdup(str.c_str());
}
public:
TypeLookupError(const TypeLookupError &other) {
Fn = other.Fn;
Context = other.Fn(other.Context, Command::CopyContext, nullptr);
}
TypeLookupError(TypeLookupError &&other) {
Fn = other.Fn;
Context = other.Context;
other.Fn = nop;
other.Context = nullptr;
}
~TypeLookupError() { Fn(Context, Command::DestroyContext, nullptr); }
TypeLookupError(void *context, Callback fn) : Context(context), Fn(fn ? fn : nop) {}
TypeLookupError &operator=(const TypeLookupError &other) {
if (this == &other)
return *this;
Fn(Context, Command::DestroyContext, nullptr);
Fn = other.Fn;
Context = Fn(Context, Command::CopyContext, nullptr);
return *this;
}
/// Construct a TypeLookupError that just returns a constant C string.
TypeLookupError(const char *str)
: TypeLookupError([=] { return const_cast<char *>(str); }) {}
/// Construct a TypeLookupError that creates a string using asprintf. The passed-in
/// format string and arguments are passed directly to swift_asprintf when
/// the string is requested. The arguments are captured and the string is only
/// formatted when needed.
template <typename... Args>
TypeLookupError(const char *fmt, Args... args)
: TypeLookupError([=] {
char *str;
swift_asprintf(&str, fmt, args...);
return str;
}) {}
/// Construct a TypeLookupError that wraps a function returning a string. The
/// passed-in function can return either a `std::string` or `char *`. If it
/// returns `char *` then the string will be destroyed with `free()`.
template <typename F> TypeLookupError(const F &fn) {
Context = new F(fn);
Fn = [](void *context, Command command, void *param) -> void * {
auto castContext = reinterpret_cast<F *>(context);
switch (command) {
case Command::CopyErrorString: {
return TypeLookupError::getCString((*castContext)());
}
case Command::DestroyErrorString:
free(param);
return nullptr;
case Command::CopyContext:
return new F(*castContext);
case Command::DestroyContext:
delete castContext;
return nullptr;
}
llvm_unreachable("unhandled command!");
};
}
/// Get the error string from the error value. The value must be passed to
/// `freeErrorString` when done. (Unless you're just calling a `fatalError`
/// in which case there's no point.)
char *copyErrorString() {
return reinterpret_cast<char *>(
Fn(Context, Command::CopyErrorString, nullptr));
}
/// Free an error string previously obtained from `copyErrorString`.
void freeErrorString(char *str) {
Fn(Context, Command::DestroyErrorString, str);
}
};
/// A value that's either a `TypeLookupError` or some parameterized type value `T`. A
/// convenience wrapper around `TaggedUnion<T, TypeLookupError>`.
template <typename T> class TypeLookupErrorOr {
TaggedUnion<T, TypeLookupError> Value;
public:
TypeLookupErrorOr(const T &t) : Value(t) {
if (!t)
Value = TypeLookupError("unknown error");
}
TypeLookupErrorOr(const TypeLookupError &te) : Value(te) {}
T getType() {
if (auto *ptr = Value.template dyn_cast<T>())
return *ptr;
return T();
}
TypeLookupError *getError() {
return Value.template dyn_cast<TypeLookupError>();
}
bool isError() { return getError() != nullptr; }
};
} // namespace swift
#endif // SWIFT_DEMANGLING_TypeLookupError_H