首页 > 解决方案 > 为什么 type(of:) 返回 Metatype,而不是 T.Type?

问题描述

我注意到type(of:)有这个意想不到的签名:

func type<T, Metatype>(of value: T) -> Metatype

而我本来期望:

func type<T>(of value: T) -> T.Type

实际签名以某种方式采用 2 个独立且无界的类型参数,将其中一个用作参数类型,另一个用作返回类型。这似乎表明我可以做一些像这样愚蠢的事情:

let foo: String = type(of: 1) // T is Int, Metatype is String

但是当我真正尝试时,

无法将“Int.Type”类型的值转换为指定类型“String”

所以我毕竟没有指定Metatype,即使它是一个泛型类型参数。我很好奇编译器是怎么做到的,所以我去源代码查了一下。我认为阻止我做那件愚蠢事情的是这个@_semantics注释:

@_transparent
@_semantics("typechecker.type(of:)")
public func type<T, Metatype>(of value: T) -> Metatype {

我还看到一些评论解释说这里写的实现实际上并没有被调用,这是由类型检查器本身直接处理的。但这并不能回答我的问题——为什么type(of:)返回一个完全不相关的类型参数Metatype,而不是T.Type

我的猜测是,在某些极端情况下,type(of:)会返回一个完全不相关的类型T,但我不知道那是什么。

标签: swifttypes

解决方案


tl;dr:的行为type(of:)取决于T是存在的还是具体的,类型系统不能在语法上有效地反映实际的返回类型,所以直接在类型检查系统中处理。Metatype在代码中明确地不被约束为相同,T以便可以专门化有效行为。Metatype并且T不一定相关。


type(of:)特殊之处在于它的行为因传递给它的类型而异。具体来说,它对存在类型有特殊的行为,因为它能够通过存在框来获取传入值的底层类型。例如:

func myType<T>(of value: T) -> T.Type {
    return T.self
}

protocol Foo {}
struct X: Foo {}

let x = X()
print(type(of: x), "vs.", myType(of: x)) // => X vs. X

let f: Foo = X()
print(type(of: f), "vs.", myType(of: f)) // => X vs. Foo

当给定一个存在类型时Foo返回类型T.Type只能返回存在本身的元类型(即),而不是存在容器内的的元类型( )。因此,不是返回,而是返回一个不相关的类型,该类型绑定到类型检查器本身中的正确类型。这是您正在寻找的边缘案例:Foo.selfX.selfT.Typetype(of:)Metadata

我的猜测是,在某些极端情况下,type(of:)会返回一个完全不相关的类型T,但我不知道那是什么。

如果您查看lib/Sema/TypeChecker.h,您可以看到几个 stdlib 函数类型的一些特殊语义声明:

/// Special-case type checking semantics for certain declarations.
enum class DeclTypeCheckingSemantics {
  /// A normal declaration.
  Normal,

  /// The type(of:) declaration, which performs a "dynamic type" operation,
  /// with different behavior for existential and non-existential arguments.
  TypeOf,

  /// The withoutActuallyEscaping(_:do:) declaration, which makes a nonescaping
  /// closure temporarily escapable.
  WithoutActuallyEscaping,

  /// The _openExistential(_:do:) declaration, which extracts the value inside
  /// an existential and passes it as a value of its own dynamic type.
  OpenExistential,
};

这里的关键之一是TypeOf,它确实是为具有@_semantics("typechecker.type(of:)")您记下的属性的函数返回的。(您可以看到该属性是如何签入的TypeChecker::getDeclTypeCheckingSemantics

如果你去寻找 的用法TypeOf,类型检查有两个关键位置:

  1. getTypeOfReferenceWithSpecialTypeCheckingSemantics它在类型检查器约束系统中注入类型约束。type(of:)在这里作为重载处理,因为Metadata实际上并没有绑定;这里的约束求解器应用了一个有效的类型检查约束,该约束约束Metadatavalue. 这里的关键是以type(of:)这种方式编写的,因此它会是一个重载,并在此处处理。
  2. ExprRewriter::finishApply它在 AST 中执行实际表达式重写,以将返回类型替换为值的有效实际类型

从(1):

// Proceed with a "DynamicType" operation. This produces an existential
// metatype from existentials, or a concrete metatype from non-
// existentials (as seen from the current abstraction level), which can't
// be expressed in the type system currently.

回顾一些历史——这是在提交1889fde2284916e2c368c9c7cc87906adae9155b中实现的。乔的提交信息很有启发性:

通过重载解析type(of:)而不是解析hackery来解决。

type(of:)具有其类型在 Swift 的类型系统中不能直接表示的行为,因为它产生了具体的和存在的元类型。在 Swift 3 中,我们添加了一个解析器 hack 来type(of: <expr>)转换为 DynamicTypeExpr,但这实际上是type(of:)一个保留名称。将其置于与其他声明相同的级别更有原则,即使它具有特殊情况的类型系统行为,如果出现在重载集中Swift.type(of:),我们可以通过对重载决策期间生成的类型系统进行特殊处理来做到这一点。Swift.type(of:)这也为处理我们希望表面上表现得像普通声明但具有其他不可表达类型的其他声明奠定了基础,即。withoutActuallyEscaping从 SE-0110。

从那时起,正如我们从WithoutActuallyEscaping和中看到的OpenExistential,其他特殊函数已被重写以利用这一点。


推荐阅读