Add error reporting when looking up types by demangled name.

This commit is contained in:
Mike Ash
2020-08-19 12:30:34 -04:00
parent 24f1a46efa
commit fd6922f92d
20 changed files with 743 additions and 377 deletions

View File

@@ -18,10 +18,12 @@
#ifndef SWIFT_DEMANGLING_TYPEDECODER_H
#define SWIFT_DEMANGLING_TYPEDECODER_H
#include "TypeLookupError.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/Basic/LLVM.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/NamespaceMacros.h"
#include "swift/Basic/LLVM.h"
#include "swift/Runtime/Portability.h"
#include "swift/Runtime/Unreachable.h"
#include "swift/Strings.h"
#include "llvm/ADT/ArrayRef.h"
@@ -317,6 +319,14 @@ getObjCClassOrProtocolName(NodePointer node) {
}
#endif
#define MAKE_NODE_TYPE_ERROR(Node, Fmt, ...) \
TypeLookupError("TypeDecoder.h:%d: Node kind %u \"%.*s\" - " Fmt, __LINE__, \
Node->getKind(), \
Node->hasText() ? (int)Node->getText().size() : 0, \
Node->hasText() ? Node->getText().data() : "", __VA_ARGS__)
#define MAKE_NODE_TYPE_ERROR0(Node, Str) MAKE_NODE_TYPE_ERROR(Node, "%s", Str)
/// Decode a mangled type to construct an abstract type, forming such
/// types by invoking a custom builder.
template <typename BuilderType>
@@ -333,24 +343,25 @@ class TypeDecoder {
: Builder(Builder) {}
/// Given a demangle tree, attempt to turn it into a type.
BuiltType decodeMangledType(NodePointer Node) {
if (!Node) return BuiltType();
TypeLookupErrorOr<BuiltType> decodeMangledType(NodePointer Node) {
if (!Node)
return TypeLookupError("Node is NULL");
using NodeKind = Demangle::Node::Kind;
switch (Node->getKind()) {
case NodeKind::Global:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children.");
return decodeMangledType(Node->getChild(0));
case NodeKind::TypeMangling:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children.");
return decodeMangledType(Node->getChild(0));
case NodeKind::Type:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children.");
return decodeMangledType(Node->getChild(0));
case NodeKind::Class:
@@ -369,8 +380,8 @@ class TypeDecoder {
BuiltTypeDecl typeDecl = BuiltTypeDecl();
BuiltType parent = BuiltType();
bool typeAlias = false;
if (!decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias))
return BuiltType();
if (auto error = decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias))
return *error;
if (typeAlias)
return Builder.createTypeAliasType(typeDecl, parent);
@@ -384,19 +395,21 @@ class TypeDecoder {
case NodeKind::BoundGenericTypeAlias:
case NodeKind::BoundGenericOtherNominalType: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
llvm::SmallVector<BuiltType, 8> args;
const auto &genericArgs = Node->getChild(1);
if (genericArgs->getKind() != NodeKind::TypeList)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList");
for (auto genericArg : *genericArgs) {
auto paramType = decodeMangledType(genericArg);
if (!paramType)
return BuiltType();
args.push_back(paramType);
if (paramType.isError())
return paramType;
args.push_back(paramType.getType());
}
auto ChildNode = Node->getChild(0);
@@ -413,9 +426,9 @@ class TypeDecoder {
BuiltTypeDecl typeDecl = BuiltTypeDecl();
BuiltType parent = BuiltType();
bool typeAlias = false;
if (!decodeMangledTypeDecl(ChildNode, typeDecl,
parent, typeAlias))
return BuiltType();
if (auto error =
decodeMangledTypeDecl(ChildNode, typeDecl, parent, typeAlias))
return *error;
return Builder.createBoundGenericType(typeDecl, args, parent);
}
@@ -445,11 +458,15 @@ class TypeDecoder {
// But when resolving it to a type, we want to *keep* the argument
// so that the parent type becomes 'S' and not 'P'.
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
const auto &genericArgs = Node->getChild(1);
if (genericArgs->getNumChildren() != 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(genericArgs,
"expected 1 generic argument, saw %u",
genericArgs->getNumChildren());
return decodeMangledType(genericArgs->getChild(0));
}
@@ -469,7 +486,7 @@ class TypeDecoder {
auto reprNode = Node->getChild(i++);
if (reprNode->getKind() != NodeKind::MetatypeRepresentation ||
!reprNode->hasText())
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(reprNode, "wrong node kind or no text");
if (reprNode->getText() == "@thin")
repr = ImplMetatypeRepresentation::Thin;
else if (reprNode->getText() == "@thick")
@@ -477,26 +494,28 @@ class TypeDecoder {
else if (reprNode->getText() == "@objc_metatype")
repr = ImplMetatypeRepresentation::ObjC;
} else if (Node->getNumChildren() < 1) {
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
}
auto instance = decodeMangledType(Node->getChild(i));
if (!instance)
return BuiltType();
if (instance.isError())
return instance;
if (Node->getKind() == NodeKind::Metatype) {
return Builder.createMetatypeType(instance, repr);
return Builder.createMetatypeType(instance.getType(), repr);
} else if (Node->getKind() == NodeKind::ExistentialMetatype) {
return Builder.createExistentialMetatypeType(instance, repr);
return Builder.createExistentialMetatypeType(instance.getType(), repr);
} else {
assert(false);
return nullptr;
return MAKE_NODE_TYPE_ERROR0(Node,
"Metatype/ExistentialMetatype Node "
"had a different kind when re-checked");
}
}
case NodeKind::ProtocolList:
case NodeKind::ProtocolListWithAnyObject:
case NodeKind::ProtocolListWithClass: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
// Find the protocol list.
llvm::SmallVector<BuiltProtocolDecl, 8> Protocols;
@@ -511,7 +530,8 @@ class TypeDecoder {
if (auto Protocol = decodeMangledProtocolType(componentType))
Protocols.push_back(Protocol);
else
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(componentType,
"failed to decode protocol type");
}
// Superclass or AnyObject, if present.
@@ -519,11 +539,15 @@ class TypeDecoder {
auto Superclass = BuiltType();
if (Node->getKind() == NodeKind::ProtocolListWithClass) {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
auto superclassNode = Node->getChild(1);
Superclass = decodeMangledType(superclassNode);
if (!Superclass) return BuiltType();
auto result = decodeMangledType(superclassNode);
if (result.isError())
return result;
Superclass = result.getType();
IsClassBound = true;
} else if (Node->getKind() == NodeKind::ProtocolListWithAnyObject) {
@@ -541,17 +565,18 @@ class TypeDecoder {
/*IsClassBound=*/false);
}
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "failed to decode protocol type");
}
case NodeKind::DynamicSelf: {
if (Node->getNumChildren() != 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node, "expected 1 child, saw %u",
Node->getNumChildren());
auto selfType = decodeMangledType(Node->getChild(0));
if (!selfType)
return BuiltType();
if (selfType.isError())
return selfType;
return Builder.createDynamicSelfType(selfType);
return Builder.createDynamicSelfType(selfType.getType());
}
case NodeKind::DependentGenericParamType: {
auto depth = Node->getChild(0)->getIndex();
@@ -571,7 +596,9 @@ class TypeDecoder {
case NodeKind::EscapingLinearFunctionType:
case NodeKind::FunctionType: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
FunctionTypeFlags flags;
if (Node->getKind() == NodeKind::ObjCBlock ||
@@ -611,13 +638,16 @@ class TypeDecoder {
flags = flags.withAsync(isAsync).withThrows(isThrow);
if (Node->getNumChildren() < firstChildIdx + 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (%u)",
Node->getNumChildren(), firstChildIdx + 2);
bool hasParamFlags = false;
llvm::SmallVector<FunctionParam<BuiltType>, 8> parameters;
if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx),
parameters, hasParamFlags))
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node->getChild(firstChildIdx),
"failed to decode function type");
flags =
flags.withNumParameters(parameters.size())
.withParameterFlags(hasParamFlags)
@@ -631,8 +661,9 @@ class TypeDecoder {
NodeKind::EscapingLinearFunctionType);
auto result = decodeMangledType(Node->getChild(firstChildIdx+1));
if (!result) return BuiltType();
return Builder.createFunctionType(parameters, result, flags);
if (result.isError())
return result;
return Builder.createFunctionType(parameters, result.getType(), flags);
}
case NodeKind::ImplFunctionType: {
auto calleeConvention = ImplParameterConvention::Direct_Unowned;
@@ -646,7 +677,7 @@ class TypeDecoder {
if (child->getKind() == NodeKind::ImplConvention) {
if (!child->hasText())
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child, "expected text");
if (child->getText() == "@convention(thin)") {
flags =
@@ -656,7 +687,7 @@ class TypeDecoder {
}
} else if (child->getKind() == NodeKind::ImplFunctionAttribute) {
if (!child->hasText())
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child, "expected text");
StringRef text = child->getText();
if (text == "@convention(c)") {
@@ -676,15 +707,18 @@ class TypeDecoder {
flags = flags.withEscaping();
} else if (child->getKind() == NodeKind::ImplParameter) {
if (decodeImplFunctionParam(child, parameters))
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child,
"failed to decode function parameter");
} else if (child->getKind() == NodeKind::ImplResult) {
if (decodeImplFunctionParam(child, results))
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child,
"failed to decode function parameter");
} else if (child->getKind() == NodeKind::ImplErrorResult) {
if (decodeImplFunctionPart(child, errorResults))
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child,
"failed to decode function part");
} else {
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(child, "unexpected kind");
}
}
@@ -696,7 +730,8 @@ class TypeDecoder {
errorResult = errorResults.front();
break;
default:
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node, "got %zu errors",
errorResults.size());
}
// TODO: Some cases not handled above, but *probably* they cannot
@@ -711,13 +746,13 @@ class TypeDecoder {
case NodeKind::ArgumentTuple:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
return decodeMangledType(Node->getChild(0));
case NodeKind::ReturnType:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
return decodeMangledType(Node->getChild(0));
@@ -726,12 +761,13 @@ class TypeDecoder {
std::string labels;
for (auto &element : *Node) {
if (element->getKind() != NodeKind::TupleElement)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind");
// If the tuple element is labeled, add its label to 'labels'.
unsigned typeChildIndex = 0;
if (element->getChild(typeChildIndex)->getKind() == NodeKind::VariadicMarker) {
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(element->getChild(typeChildIndex),
"no children");
}
if (element->getChild(typeChildIndex)->getKind() == NodeKind::TupleElementName) {
// Add spaces to terminate all the previous labels if this
@@ -749,22 +785,23 @@ class TypeDecoder {
}
// Decode the element type.
BuiltType elementType =
decodeMangledType(element->getChild(typeChildIndex));
if (!elementType)
return BuiltType();
auto elementType = decodeMangledType(element->getChild(typeChildIndex));
if (elementType.isError())
return elementType;
elements.push_back(elementType);
elements.push_back(elementType.getType());
}
return Builder.createTupleType(elements, std::move(labels));
}
case NodeKind::TupleElement:
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
if (Node->getChild(0)->getKind() == NodeKind::TupleElementName) {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
return decodeMangledType(Node->getChild(1));
}
@@ -772,68 +809,75 @@ class TypeDecoder {
case NodeKind::DependentGenericType: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
return decodeMangledType(Node->getChild(1));
}
case NodeKind::DependentMemberType: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
if (base.isError())
return base;
auto assocTypeChild = Node->getChild(1);
auto member = assocTypeChild->getFirstChild()->getText();
if (assocTypeChild->getNumChildren() < 2)
return Builder.createDependentMemberType(member.str(), base);
return Builder.createDependentMemberType(member.str(), base.getType());
auto protocol = decodeMangledProtocolType(assocTypeChild->getChild(1));
if (!protocol)
return BuiltType();
return Builder.createDependentMemberType(member.str(), base, protocol);
return Builder.createDependentMemberType(member.str(), base.getType(),
protocol);
}
case NodeKind::DependentAssociatedTypeRef: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
return decodeMangledType(Node->getChild(1));
}
case NodeKind::Unowned: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
return Builder.createUnownedStorageType(base);
if (base.isError())
return base;
return Builder.createUnownedStorageType(base.getType());
}
case NodeKind::Unmanaged: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
return Builder.createUnmanagedStorageType(base);
if (base.isError())
return base;
return Builder.createUnmanagedStorageType(base.getType());
}
case NodeKind::Weak: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
return Builder.createWeakStorageType(base);
if (base.isError())
return base;
return Builder.createWeakStorageType(base.getType());
}
case NodeKind::SILBoxType: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
return Builder.createSILBoxType(base);
if (base.isError())
return base;
return Builder.createSILBoxType(base.getType());
}
case NodeKind::SILBoxTypeWithLayout: {
// TODO: Implement SILBoxTypeRefs with layout. As a stopgap, specify the
@@ -842,57 +886,62 @@ class TypeDecoder {
}
case NodeKind::SugaredOptional: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
if (base.isError())
return base;
return Builder.createOptionalType(base);
return Builder.createOptionalType(base.getType());
}
case NodeKind::SugaredArray: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
if (base.isError())
return base;
return Builder.createArrayType(base);
return Builder.createArrayType(base.getType());
}
case NodeKind::SugaredDictionary: {
if (Node->getNumChildren() < 2)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (2)",
Node->getNumChildren());
auto key = decodeMangledType(Node->getChild(0));
if (!key)
return BuiltType();
if (key.isError())
return key;
auto value = decodeMangledType(Node->getChild(1));
if (!key)
return BuiltType();
if (value.isError())
return value;
return Builder.createDictionaryType(key, value);
return Builder.createDictionaryType(key.getType(), value.getType());
}
case NodeKind::SugaredParen: {
if (Node->getNumChildren() < 1)
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "no children");
auto base = decodeMangledType(Node->getChild(0));
if (!base)
return BuiltType();
if (base.isError())
return base;
return Builder.createParenType(base);
return Builder.createParenType(base.getType());
}
case NodeKind::OpaqueType: {
if (Node->getNumChildren() < 3)
return BuiltType();
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%u) than required (3)",
Node->getNumChildren());
auto descriptor = Node->getChild(0);
auto ordinalNode = Node->getChild(1);
if (ordinalNode->getKind() != NodeKind::Index
|| !ordinalNode->hasIndex())
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(ordinalNode,
"unexpected kind or no index");
auto ordinal = ordinalNode->getIndex();
std::vector<BuiltType> genericArgsBuf;
@@ -905,9 +954,9 @@ class TypeDecoder {
break;
for (auto argNode : *genericsNode) {
auto arg = decodeMangledType(argNode);
if (!arg)
return BuiltType();
genericArgsBuf.push_back(arg);
if (arg.isError())
return arg;
genericArgsBuf.push_back(arg.getType());
}
}
genericArgsLevels.push_back(genericArgsBuf.size());
@@ -923,7 +972,7 @@ class TypeDecoder {
// TODO: Handle OpaqueReturnType, when we're in the middle of reconstructing
// the defining decl
default:
return BuiltType();
return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind");
}
}
@@ -943,11 +992,11 @@ private:
T::getConventionFromString(conventionString);
if (!convention)
return true;
BuiltType type = decodeMangledType(node->getChild(1));
if (!type)
auto type = decodeMangledType(node->getChild(1));
if (type.isError())
return true;
results.emplace_back(type, *convention);
results.emplace_back(type.getType(), *convention);
return false;
}
@@ -968,8 +1017,8 @@ private:
auto convention = T::getConventionFromString(conventionString);
if (!convention)
return true;
BuiltType type = decodeMangledType(typeNode);
if (!type)
auto result = decodeMangledType(typeNode);
if (result.isError())
return true;
auto diffKind = T::DifferentiabilityType::DifferentiableOrNotApplicable;
@@ -984,14 +1033,13 @@ private:
diffKind = *optDiffKind;
}
results.emplace_back(type, *convention, diffKind);
results.emplace_back(result.getType(), *convention, diffKind);
return false;
}
bool decodeMangledTypeDecl(Demangle::NodePointer node,
BuiltTypeDecl &typeDecl,
BuiltType &parent,
bool &typeAlias) {
llvm::Optional<TypeLookupError>
decodeMangledTypeDecl(Demangle::NodePointer node, BuiltTypeDecl &typeDecl,
BuiltType &parent, bool &typeAlias) {
if (node->getKind() == NodeKind::Type)
return decodeMangledTypeDecl(node->getChild(0), typeDecl,
parent, typeAlias);
@@ -1002,7 +1050,9 @@ private:
declNode = node;
} else {
if (node->getNumChildren() < 2)
return false;
return MAKE_NODE_TYPE_ERROR(
node, "Number of node children (%u) less than required (2)",
node->getNumChildren());
auto parentContext = node->getChild(0);
@@ -1018,11 +1068,14 @@ private:
case Node::Kind::Extension:
// Decode the type being extended.
if (parentContext->getNumChildren() < 2)
return false;
return MAKE_NODE_TYPE_ERROR(parentContext,
"Number of parentContext children (%u) "
"less than required (2)",
node->getNumChildren());
parentContext = parentContext->getChild(1);
LLVM_FALLTHROUGH;
default:
parent = decodeMangledType(parentContext);
parent = decodeMangledType(parentContext).getType();
// Remove any generic arguments from the context node, producing a
// node that references the nominal type declaration.
declNode = Demangle::getUnspecialized(node, Builder.getNodeFactory());
@@ -1030,9 +1083,10 @@ private:
}
}
typeDecl = Builder.createTypeDecl(declNode, typeAlias);
if (!typeDecl) return false;
if (!typeDecl)
return TypeLookupError("Failed to create type decl");
return true;
return llvm::None;
}
BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node) {
@@ -1097,10 +1151,10 @@ private:
}
auto paramType = decodeMangledType(node);
if (!paramType)
if (paramType.isError())
return false;
param.setType(paramType);
param.setType(paramType.getType());
return true;
};
@@ -1158,14 +1212,12 @@ private:
}
};
template<typename BuilderType>
inline typename BuilderType::BuiltType
decodeMangledType(BuilderType &Builder,
NodePointer Node) {
template <typename BuilderType>
inline TypeLookupErrorOr<typename BuilderType::BuiltType>
decodeMangledType(BuilderType &Builder, NodePointer Node) {
return TypeDecoder<BuilderType>(Builder).decodeMangledType(Node);
}
SWIFT_END_INLINE_NAMESPACE
} // end namespace Demangle
} // end namespace swift

View File

@@ -0,0 +1,198 @@
//===--- 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;
}
};
}
/// 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