//===--- ForeignErrorConvention.h - Error conventions -----------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // //===----------------------------------------------------------------------===// // // This file defines the ForeignErrorConvention structure, which // describes the rules for how to detect that a foreign API returns an // error. // //===----------------------------------------------------------------------===// #ifndef SWIFT_FOREIGN_ERROR_CONVENTION_H #define SWIFT_FOREIGN_ERROR_CONVENTION_H #include "swift/AST/Type.h" namespace swift { /// A small structure describing the error convention of a foreign declaration. class ForeignErrorConvention { public: enum Kind { /// An error is indicated by a zero result. The function has /// been imported as returning Void. /// /// This convention provides a result type, which has a single /// field of integer type. ZeroResult, /// An error is indicated by a non-zero result. The function has /// been imported as returning Void. /// /// This convention provides a result type, which has a single /// field of integer type. NonZeroResult, /// An error is indicated by a zero result, but the rest of the /// result is meaningful. ZeroPreservedResult, /// An error is indicated by a nil result. The function has been /// imported as returning a non-optional type (which is not /// address-only). NilResult, /// An error is indicated by a non-nil error value being left in /// the error parameter. NonNilError, }; /// Is the error owned by the caller, given that it exists? enum IsOwned_t : bool { IsNotOwned = false, IsOwned = true }; /// Was the error parameter replaced by () or just removed? enum IsReplaced_t : bool { IsNotReplaced = false, IsReplaced = true }; struct Info { unsigned TheKind : 8; unsigned ErrorIsOwned : 1; unsigned ErrorParameterIsReplaced : 1; unsigned ErrorParameterIndex : 22; Info(Kind kind, unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced) : TheKind(unsigned(kind)), ErrorIsOwned(bool(isOwned)), ErrorParameterIsReplaced(bool(isReplaced)), ErrorParameterIndex(parameterIndex) { assert(parameterIndex == ErrorParameterIndex && "parameter index overflowed"); } Info() = default; Kind getKind() const { return static_cast(TheKind); } }; private: Info info; /// The error parameter type. This is currently assumed to be an /// indirect out-parameter. CanType ErrorParameterType; /// The result type. This is meaningful only for ZeroResult and /// NonZeroResult. CanType ResultType; ForeignErrorConvention(Kind kind, unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType, CanType resultType = CanType()) : info(kind, parameterIndex, isOwned, isReplaced), ErrorParameterType(parameterType), ResultType(resultType) {} public: static ForeignErrorConvention getZeroResult(unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType, CanType resultType) { return { ZeroResult, parameterIndex, isOwned, isReplaced, parameterType, resultType }; } static ForeignErrorConvention getNonZeroResult(unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType, CanType resultType) { return { NonZeroResult, parameterIndex, isOwned, isReplaced, parameterType, resultType }; } static ForeignErrorConvention getZeroPreservedResult(unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType) { return { ZeroPreservedResult, parameterIndex, isOwned, isReplaced, parameterType }; } static ForeignErrorConvention getNilResult(unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType) { return { NilResult, parameterIndex, isOwned, isReplaced, parameterType }; } static ForeignErrorConvention getNonNilError(unsigned parameterIndex, IsOwned_t isOwned, IsReplaced_t isReplaced, CanType parameterType) { return { NonNilError, parameterIndex, isOwned, isReplaced, parameterType }; } /// Returns the error convention in use. Kind getKind() const { return Kind(info.TheKind); } /// Returns true if this convention strips a layer of optionality /// from the formal result type. bool stripsResultOptionality() const { return getKind() == Kind::NilResult; } /// Returns the index of the error parameter. unsigned getErrorParameterIndex() const { return info.ErrorParameterIndex; } /// Has the error parameter been replaced with void? IsReplaced_t isErrorParameterReplacedWithVoid() const { return IsReplaced_t(info.ErrorParameterIsReplaced); } /// Returns whether the error result is owned. It's assumed that the /// error parameter should be ignored if no error is present (unless /// it needs to be checked explicitly to determine that). IsOwned_t isErrorOwned() const { return IsOwned_t(info.ErrorIsOwned); } /// Returns the type of the error parameter. Assumed to be an /// UnsafeMutablePointer for some T. CanType getErrorParameterType() const { return ErrorParameterType; } /// Returns the physical result type of the function, for functions /// that completely erase this information. CanType getResultType() const { assert(resultTypeErasedToVoid(getKind())); return ResultType; } /// Whether this kind of error import erases the result type to 'Void'. static bool resultTypeErasedToVoid(Kind kind) { switch (kind) { case ZeroResult: case NonZeroResult: return true; case ZeroPreservedResult: case NilResult: case NonNilError: return false; } llvm_unreachable("unhandled foreign error kind!"); } bool operator==(ForeignErrorConvention other) const { return info.TheKind == other.info.TheKind && info.ErrorIsOwned == other.info.ErrorIsOwned && info.ErrorParameterIsReplaced == other.info.ErrorParameterIsReplaced && info.ErrorParameterIndex == other.info.ErrorParameterIndex; } bool operator!=(ForeignErrorConvention other) const { return !(*this == other); } }; } #endif