Skip to content

Commit 50ea66d

Browse files
authored
Merge pull request #33585 from mikeash/type-lookup-error-reporting
Add error reporting when looking up types by demangled name.
2 parents 8df8315 + fd6922f commit 50ea66d

File tree

20 files changed

+743
-377
lines changed

20 files changed

+743
-377
lines changed

include/swift/Demangling/TypeDecoder.h

Lines changed: 179 additions & 127 deletions
Large diffs are not rendered by default.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//===--- TypeLookupError.h - Type lookup error value. -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Provides the TypeLookupError class, which represents errors when demangling
14+
// or looking up types.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_DEMANGLING_TypeLookupError_H
19+
#define SWIFT_DEMANGLING_TypeLookupError_H
20+
21+
#include "swift/Basic/TaggedUnion.h"
22+
#include "swift/Runtime/Portability.h"
23+
24+
namespace swift {
25+
26+
/// An error that occurred while looking up a type at runtime from a mangled
27+
/// name.
28+
/// ///
29+
/// This ultimately just provides a string, but is built to take up minimal
30+
/// space when passed around, and perform as much work lazily as possible. We
31+
/// don't want to spend a lot of time building strings when the caller is going
32+
/// to handle the error gracefully and try a fallback. We only want to waste
33+
/// time/space on that when the error message is actually relevant, so build it
34+
/// as late as possible.
35+
/// ///
36+
/// To be as compact as possible, this type holds a context pointer and a
37+
/// callback function. The callback function uses the context pointer to return
38+
/// an error string when requested. The callback function also does quadruple
39+
/// duty to copy/destroy the context as needed, and free the returned error
40+
/// string if needed. Commands are passed to the callback to request the
41+
/// different operations, which means we only have to store one function pointer
42+
/// instead of four.
43+
class TypeLookupError {
44+
public:
45+
/// The commands that can be passed to the callback function.
46+
enum class Command {
47+
/// Return the error string to the caller, as a char *.
48+
CopyErrorString,
49+
50+
/// Destroy the error string returned from CopyErrorString, if necessary.
51+
/// The return value is ignored.
52+
DestroyErrorString,
53+
54+
/// Return a copy of the context pointer (used for copying TypeLookupError
55+
/// objects.)
56+
CopyContext,
57+
58+
/// Destroy the context pointer. The return value is ignored.
59+
DestroyContext,
60+
};
61+
62+
/// The callback used to respond to the commands. The parameters are:
63+
/// - `context`: the context that the value was initialized with, or the
64+
/// context returned from a CopyContext call
65+
/// - `command`: the command to respond to
66+
/// - `param`: when `command` is `DestroyErrorString`, the string pointer to
67+
/// destroy, otherwise NULL
68+
using Callback = void *(*)(void *context, Command command, void *param);
69+
70+
private:
71+
void *Context;
72+
Callback Fn;
73+
74+
/// A no-op callback used to avoid a bunch of `if (Fn)` checks.
75+
static void *nop(void *context, Command command, void *param) {
76+
return nullptr;
77+
}
78+
79+
/// Helper functions for getting a C string from a lambda. These allow us to
80+
/// wrap lambdas returning `char *` or `std::string` and standardize them on
81+
/// `char *`.
82+
static char *getCString(char *str) { return str; }
83+
84+
static char *getCString(const std::string &str) {
85+
return strdup(str.c_str());
86+
}
87+
88+
public:
89+
TypeLookupError(const TypeLookupError &other) {
90+
Fn = other.Fn;
91+
Context = other.Fn(other.Context, Command::CopyContext, nullptr);
92+
}
93+
94+
TypeLookupError(TypeLookupError &&other) {
95+
Fn = other.Fn;
96+
Context = other.Context;
97+
98+
other.Fn = nop;
99+
other.Context = nullptr;
100+
}
101+
102+
~TypeLookupError() { Fn(Context, Command::DestroyContext, nullptr); }
103+
104+
TypeLookupError(void *context, Callback fn) : Context(context), Fn(fn ? fn : nop) {}
105+
106+
TypeLookupError &operator=(const TypeLookupError &other) {
107+
if (this == &other)
108+
return *this;
109+
110+
Fn(Context, Command::DestroyContext, nullptr);
111+
Fn = other.Fn;
112+
Context = Fn(Context, Command::CopyContext, nullptr);
113+
114+
return *this;
115+
}
116+
117+
/// Construct a TypeLookupError that just returns a constant C string.
118+
TypeLookupError(const char *str)
119+
: TypeLookupError([=] { return const_cast<char *>(str); }) {}
120+
121+
/// Construct a TypeLookupError that creates a string using asprintf. The passed-in
122+
/// format string and arguments are passed directly to swift_asprintf when
123+
/// the string is requested. The arguments are captured and the string is only
124+
/// formatted when needed.
125+
template <typename... Args>
126+
TypeLookupError(const char *fmt, Args... args)
127+
: TypeLookupError([=] {
128+
char *str;
129+
swift_asprintf(&str, fmt, args...);
130+
return str;
131+
}) {}
132+
133+
/// Construct a TypeLookupError that wraps a function returning a string. The
134+
/// passed-in function can return either a `std::string` or `char *`. If it
135+
/// returns `char *` then the string will be destroyed with `free()`.
136+
template <typename F> TypeLookupError(const F &fn) {
137+
Context = new F(fn);
138+
Fn = [](void *context, Command command, void *param) -> void * {
139+
auto castContext = reinterpret_cast<F *>(context);
140+
switch (command) {
141+
case Command::CopyErrorString: {
142+
return TypeLookupError::getCString((*castContext)());
143+
}
144+
case Command::DestroyErrorString:
145+
free(param);
146+
return nullptr;
147+
case Command::CopyContext:
148+
return new F(*castContext);
149+
case Command::DestroyContext:
150+
delete castContext;
151+
return nullptr;
152+
}
153+
};
154+
}
155+
156+
/// Get the error string from the error value. The value must be passed to
157+
/// `freeErrorString` when done. (Unless you're just calling a `fatalError`
158+
/// in which case there's no point.)
159+
char *copyErrorString() {
160+
return reinterpret_cast<char *>(
161+
Fn(Context, Command::CopyErrorString, nullptr));
162+
}
163+
164+
/// Free an error string previously obtained from `copyErrorString`.
165+
void freeErrorString(char *str) {
166+
Fn(Context, Command::DestroyErrorString, str);
167+
}
168+
};
169+
170+
/// A value that's either a `TypeLookupError` or some parameterized type value `T`. A
171+
/// convenience wrapper around `TaggedUnion<T, TypeLookupError>`.
172+
template <typename T> class TypeLookupErrorOr {
173+
TaggedUnion<T, TypeLookupError> Value;
174+
175+
public:
176+
TypeLookupErrorOr(const T &t) : Value(t) {
177+
if (!t)
178+
Value = TypeLookupError("unknown error");
179+
}
180+
181+
TypeLookupErrorOr(const TypeLookupError &te) : Value(te) {}
182+
183+
T getType() {
184+
if (auto *ptr = Value.template dyn_cast<T>())
185+
return *ptr;
186+
return T();
187+
}
188+
189+
TypeLookupError *getError() {
190+
return Value.template dyn_cast<TypeLookupError>();
191+
}
192+
193+
bool isError() { return getError() != nullptr; }
194+
};
195+
196+
} // namespace swift
197+
198+
#endif // SWIFT_DEMANGLING_TypeLookupError_H

include/swift/Reflection/TypeRefBuilder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,8 @@ class TypeRefBuilder {
618618
}),
619619
OpaqueUnderlyingTypeReader(
620620
[&reader](uint64_t descriptorAddr, unsigned ordinal) -> const TypeRef* {
621-
return reader.readUnderlyingTypeForOpaqueTypeDescriptor(descriptorAddr,
622-
ordinal);
621+
return reader.readUnderlyingTypeForOpaqueTypeDescriptor(
622+
descriptorAddr, ordinal).getType();
623623
})
624624
{}
625625

include/swift/Remote/MetadataReader.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,8 @@ class MetadataReader {
463463
}
464464

465465
/// Given a demangle tree, attempt to turn it into a type.
466-
BuiltType decodeMangledType(NodePointer Node) {
466+
TypeLookupErrorOr<typename BuilderType::BuiltType>
467+
decodeMangledType(NodePointer Node) {
467468
return swift::Demangle::decodeMangledType(Builder, Node);
468469
}
469470

@@ -925,8 +926,8 @@ class MetadataReader {
925926
swift_runtime_unreachable("Unhandled MetadataKind in switch");
926927
}
927928

928-
BuiltType readTypeFromMangledName(const char *MangledTypeName,
929-
size_t Length) {
929+
TypeLookupErrorOr<typename BuilderType::BuiltType>
930+
readTypeFromMangledName(const char *MangledTypeName, size_t Length) {
930931
Demangle::Demangler Dem;
931932
Demangle::NodePointer Demangled =
932933
Dem.demangleSymbol(StringRef(MangledTypeName, Length));
@@ -1183,14 +1184,14 @@ class MetadataReader {
11831184
MangledNameKind::Type, Dem);
11841185
}
11851186

1186-
BuiltType
1187+
TypeLookupErrorOr<typename BuilderType::BuiltType>
11871188
readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr,
11881189
unsigned ordinal) {
11891190
Demangle::Demangler Dem;
11901191
auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr,
11911192
ordinal, Dem);
11921193
if (!node)
1193-
return BuiltType();
1194+
return TypeLookupError("Failed to read type mangling for descriptor.");
11941195
return decodeMangledType(node);
11951196
}
11961197

include/swift/Runtime/Debug.h

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "swift/Runtime/Unreachable.h"
2222
#include <atomic>
2323
#include <cstdarg>
24-
#include <cstdio>
2524
#include <functional>
2625
#include <stdint.h>
2726

@@ -248,39 +247,6 @@ std::atomic<const void *> _swift_debug_metadataAllocationBacktraceList;
248247
SWIFT_RUNTIME_STDLIB_SPI
249248
const void * const _swift_debug_protocolConformanceStatePointer;
250249

251-
SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE
252-
inline static int swift_asprintf(char **strp, const char *fmt, ...) {
253-
va_list args;
254-
va_start(args, fmt);
255-
#if defined(_WIN32)
256-
#pragma GCC diagnostic push
257-
#pragma GCC diagnostic ignored "-Wuninitialized"
258-
int len = _vscprintf(fmt, args);
259-
#pragma GCC diagnostic pop
260-
if (len < 0) {
261-
va_end(args);
262-
return -1;
263-
}
264-
char *buffer = static_cast<char *>(malloc(len + 1));
265-
if (!buffer) {
266-
va_end(args);
267-
return -1;
268-
}
269-
int result = vsprintf(buffer, fmt, args);
270-
if (result < 0) {
271-
va_end(args);
272-
free(buffer);
273-
return -1;
274-
}
275-
*strp = buffer;
276-
#else
277-
int result = vasprintf(strp, fmt, args);
278-
#endif
279-
va_end(args);
280-
return result;
281-
}
282-
283-
284250
// namespace swift
285251
}
286252

include/swift/Runtime/Portability.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,47 @@
1616

1717
#ifndef SWIFT_RUNTIME_PORTABILITY_H
1818
#define SWIFT_RUNTIME_PORTABILITY_H
19+
20+
#include <cstdarg>
1921
#include <cstddef>
22+
#include <cstdio>
23+
#include <cstdlib>
2024

2125
size_t _swift_strlcpy(char *dst, const char *src, size_t maxlen);
2226

27+
// Skip the attribute when included by the compiler.
28+
#ifdef SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE
29+
SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE
30+
#endif
31+
inline static int swift_asprintf(char **strp, const char *fmt, ...) {
32+
va_list args;
33+
va_start(args, fmt);
34+
#if defined(_WIN32)
35+
#pragma GCC diagnostic push
36+
#pragma GCC diagnostic ignored "-Wuninitialized"
37+
int len = _vscprintf(fmt, args);
38+
#pragma GCC diagnostic pop
39+
if (len < 0) {
40+
va_end(args);
41+
return -1;
42+
}
43+
char *buffer = static_cast<char *>(malloc(len + 1));
44+
if (!buffer) {
45+
va_end(args);
46+
return -1;
47+
}
48+
int result = vsprintf(buffer, fmt, args);
49+
if (result < 0) {
50+
va_end(args);
51+
free(buffer);
52+
return -1;
53+
}
54+
*strp = buffer;
55+
#else
56+
int result = vasprintf(strp, fmt, args);
57+
#endif
58+
va_end(args);
59+
return result;
60+
}
61+
2362
#endif

lib/AST/ASTDemangler.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Type swift::Demangle::getTypeForMangling(ASTContext &ctx,
4444
return Type();
4545

4646
ASTBuilder builder(ctx);
47-
return swift::Demangle::decodeMangledType(builder, node);
47+
return swift::Demangle::decodeMangledType(builder, node).getType();
4848
}
4949

5050
TypeDecl *swift::Demangle::getTypeDeclForMangling(ASTContext &ctx,
@@ -846,8 +846,8 @@ CanGenericSignature ASTBuilder::demangleGenericSignature(
846846

847847
if (child->getNumChildren() != 2)
848848
return CanGenericSignature();
849-
auto subjectType = swift::Demangle::decodeMangledType(
850-
*this, child->getChild(0));
849+
auto subjectType =
850+
swift::Demangle::decodeMangledType(*this, child->getChild(0)).getType();
851851
if (!subjectType)
852852
return CanGenericSignature();
853853

@@ -856,8 +856,9 @@ CanGenericSignature ASTBuilder::demangleGenericSignature(
856856
Demangle::Node::Kind::DependentGenericConformanceRequirement ||
857857
child->getKind() ==
858858
Demangle::Node::Kind::DependentGenericSameTypeRequirement) {
859-
constraintType = swift::Demangle::decodeMangledType(
860-
*this, child->getChild(1));
859+
constraintType =
860+
swift::Demangle::decodeMangledType(*this, child->getChild(1))
861+
.getType();
861862
if (!constraintType)
862863
return CanGenericSignature();
863864
}

lib/RemoteAST/RemoteAST.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl {
632632
SubstitutionMap substitutions,
633633
unsigned ordinal) override {
634634
auto underlyingType = Reader
635-
.readUnderlyingTypeForOpaqueTypeDescriptor(opaqueDescriptor.getAddressData(),
636-
ordinal);
637-
635+
.readUnderlyingTypeForOpaqueTypeDescriptor(
636+
opaqueDescriptor.getAddressData(), ordinal)
637+
.getType();
638+
638639
if (!underlyingType)
639640
return getFailure<Type>();
640641

0 commit comments

Comments
 (0)