blob: 6dcb9a6516ef1319f07b8126c79cea48a660ad83 [file] [log] [blame]
//===--- 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"
#include <string.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) {
Context = const_cast<char *>(str);
Fn = [](void *context, Command command, void *param) -> void * {
// The context pointer is the string and works for both copying the string
// and copying the context. Other commands don't need to do anything.
return context;
};
}
/// 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