mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
59adfcd02b
A type variable that represents the type of a closure can only be bound to a function type, but this fact is not directly encoded in the constraint system. Check for the appearance of a non-sensical subtype binding on a closure type variable in reduceBinding(), and promote the binding to exact as soon as we detect this, since binding the type will always fail; we want to fail as quickly as possible, before attempting any more disjunctions. This is a generally good performance optimization, and it also addresses a performance regression from "Sema: Filter bindings by considering conformance constraints". This also speeds up the expression from rdar://59008707, which also uses Combine and is slow for similar reasons.
167 lines
5.1 KiB
C++
167 lines
5.1 KiB
C++
//===--- Subtyping.h - Swift subtyping and conversion rules -----*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2026 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 implements various utilities for reasoning about the Swift
|
|
// subtyping relation.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef SWIFT_SEMA_SUBTYPING_H
|
|
#define SWIFT_SEMA_SUBTYPING_H
|
|
|
|
#include "swift/Basic/OptionSet.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
namespace swift {
|
|
|
|
class GenericSignature;
|
|
class ProtocolDecl;
|
|
class Type;
|
|
|
|
namespace constraints {
|
|
|
|
class ConstraintSystem;
|
|
|
|
/// Checks if two types can unify if we record a bind constraint between them.
|
|
///
|
|
/// Returns:
|
|
/// - true if there is some indication that the bind may succeed.
|
|
/// - false if the bind will definitely fail.
|
|
/// - std::nullopt if unknown.
|
|
std::optional<bool> isLikelyExactMatch(Type first, Type second);
|
|
|
|
enum class ConversionBehavior : unsigned {
|
|
/// Most nominal types, archetypes, empty tuple.
|
|
None,
|
|
|
|
/// String has no proper subtypes, but it has pointers as supertypes.
|
|
String,
|
|
|
|
/// Classes can have subclasses and superclasses.
|
|
Class,
|
|
|
|
/// AnyHashable is a supertype to all Hashable types.
|
|
AnyHashable,
|
|
|
|
/// CGFloat and Double.
|
|
Double,
|
|
|
|
/// We fold all pointer types into one for now. These have arrays and
|
|
/// strings as subtypes.
|
|
Pointer,
|
|
|
|
/// Arrays convert to arrays, and they have pointers as supertypes.
|
|
Array,
|
|
|
|
/// Dictionaries convert to dictionaries.
|
|
Dictionary,
|
|
|
|
/// Sets convert to sets.
|
|
Set,
|
|
|
|
/// Optionals convert to Optionals. Every type has an Optional of itself
|
|
/// as a supertype.
|
|
Optional,
|
|
|
|
/// Function types and metatypes.
|
|
Structural,
|
|
|
|
/// Tuples.
|
|
Tuple,
|
|
|
|
/// InOut types have Pointer types as supertypes.
|
|
InOut,
|
|
|
|
/// LValue types convert to InOut types. Every type has a subtype that is
|
|
/// the lvalue of itself.
|
|
LValue,
|
|
|
|
/// Everything else that we don't reason about for now, like
|
|
/// existentials and tuples.
|
|
Unknown
|
|
};
|
|
|
|
/// Classify the possible conversions having this type as result type.
|
|
ConversionBehavior getConversionBehavior(Type type);
|
|
|
|
/// Suppose we are given a type T and a protocol P, and T conv U for
|
|
/// some type U; if U conforms to P, does it follow that T conforms to P?
|
|
bool checkTransitiveSupertypeConformance(ConstraintSystem &cs,
|
|
Type type, ProtocolDecl *proto);
|
|
|
|
/// Suppose we are given a type T and a protocol P, and U conv T for
|
|
/// some type U; if U conforms to P, does it follow that T conforms to P?
|
|
bool checkTransitiveSubtypeConformance(ConstraintSystem &cs,
|
|
Type type, ProtocolDecl *proto);
|
|
|
|
/// Check if there exist any subtypes of the given type, other than
|
|
/// the type itself. If the type contains type variables, this will
|
|
/// give a conservative approximation.
|
|
bool hasProperSubtypes(Type type);
|
|
|
|
/// Check if there exist any supertypes of the given type, other than
|
|
/// the type itself, and those that every type T has, that is,
|
|
/// Optional<T> and existentials.
|
|
bool hasProperSupertypes(Type type);
|
|
|
|
/// Check if this type can be a supertype of a function type. This
|
|
/// is true if it is already a function type, an existential that
|
|
/// only involves marker protocols, or an optional of the above.
|
|
///
|
|
/// Note that an lvalue type is never a supertype of an rvalue type
|
|
/// such as a function type, so given an lvalue type, this will
|
|
/// always return false. If this is not intended behavior, unwrap
|
|
/// the lvalue type first before calling this entry point.
|
|
bool isPossibleSupertypeOfFunctionType(Type type);
|
|
|
|
enum ConflictFlag : unsigned {
|
|
Category = 1 << 0,
|
|
Exact = 1 << 1,
|
|
Class = 1 << 2,
|
|
Structural = 1 << 3,
|
|
Array = 1 << 4,
|
|
DictionaryKey = 1 << 5,
|
|
DictionaryValue = 1 << 6,
|
|
Set = 1 << 7,
|
|
Optional = 1 << 8,
|
|
Double = 1 << 9,
|
|
Conformance = 1 << 10,
|
|
TupleArity = 1 << 11,
|
|
TupleElement = 1 << 11
|
|
};
|
|
using ConflictReason = OptionSet<ConflictFlag>;
|
|
|
|
void simple_display(llvm::raw_ostream &out, ConflictReason reason);
|
|
|
|
/// Check whether lhs, as a type with type variables or unopened type
|
|
/// parameters, might be a subtype of rhs, which again is a type with
|
|
/// type variables or unopened type parameters.
|
|
///
|
|
/// The type parameters are interpreted with respect to sig, whereas
|
|
/// type variables are just assumed opaque.
|
|
///
|
|
/// The answer is conservative, so we err on the side of saying that
|
|
/// a convesion _can_ happen. We only return a non-empty ConflictReason
|
|
/// if the conversion will definitely fail.
|
|
///
|
|
/// Even if the types do not contain type variables or type parameters,
|
|
/// this does not give a completely accurate answer, yet.
|
|
ConflictReason canPossiblyConvertTo(
|
|
ConstraintSystem &cs,
|
|
Type lhs, Type rhs,
|
|
GenericSignature sig);
|
|
|
|
} // end namespace constraints
|
|
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_SEMA_SUBTYPING_H
|