blob: 61a9c220dc544964725ce0d15e55af409b7b1bcb [file] [log] [blame]
//===--- Requirement.h - Swift Requirement ASTs -----------------*- 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 Requirement class and subclasses.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_REQUIREMENT_H
#define SWIFT_AST_REQUIREMENT_H
#include "swift/AST/Type.h"
#include "swift/Basic/Debug.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/Support/ErrorHandling.h"
namespace swift {
/// Describes the kind of a requirement that occurs within a requirements
/// clause.
enum class RequirementKind : unsigned {
/// A conformance requirement T : P, where T is a type that depends
/// on a generic parameter and P is a protocol to which T must conform.
Conformance,
/// A superclass requirement T : C, where T is a type that depends
/// on a generic parameter and C is a concrete class type which T must
/// equal or be a subclass of.
Superclass,
/// A same-type requirement T == U, where T and U are types that shall be
/// equivalent.
SameType,
/// A layout bound T : L, where T is a type that depends on a generic
/// parameter and L is some layout specification that should bound T.
Layout,
// Note: there is code that packs this enum in a 2-bit bitfield. Audit users
// when adding enumerators.
};
/// A single requirement placed on the type parameters (or associated
/// types thereof) of a
class Requirement {
llvm::PointerIntPair<Type, 3, RequirementKind> FirstTypeAndKind;
/// The second element of the requirement. Its content is dependent
/// on the requirement kind.
/// The payload of the following enum should always match the kind!
/// Any access to the fields of this enum should first check if the
/// requested access matches the kind of the requirement.
union {
Type SecondType;
LayoutConstraint SecondLayout;
};
public:
/// Create a conformance or same-type requirement.
Requirement(RequirementKind kind, Type first, Type second)
: FirstTypeAndKind(first, kind), SecondType(second) {
assert(first);
assert(second);
}
/// Create a layout constraint requirement.
Requirement(RequirementKind kind, Type first, LayoutConstraint second)
: FirstTypeAndKind(first, kind), SecondLayout(second) {
assert(first);
assert(second);
}
/// Determine the kind of requirement.
RequirementKind getKind() const { return FirstTypeAndKind.getInt(); }
/// Retrieve the first type.
Type getFirstType() const {
return FirstTypeAndKind.getPointer();
}
/// Retrieve the second type.
Type getSecondType() const {
assert(getKind() != RequirementKind::Layout);
return SecondType;
}
/// Subst the types involved in this requirement.
///
/// The \c args arguments are passed through to Type::subst. This doesn't
/// touch the superclasses, protocols or layout constraints.
template <typename... Args>
Optional<Requirement> subst(Args &&... args) const {
auto newFirst = getFirstType().subst(std::forward<Args>(args)...);
if (newFirst->hasError())
return None;
switch (getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Superclass:
case RequirementKind::SameType: {
auto newSecond = getSecondType().subst(std::forward<Args>(args)...);
if (newSecond->hasError())
return None;
return Requirement(getKind(), newFirst, newSecond);
}
case RequirementKind::Layout:
return Requirement(getKind(), newFirst, getLayoutConstraint());
}
llvm_unreachable("Unhandled RequirementKind in switch.");
}
/// Retrieve the layout constraint.
LayoutConstraint getLayoutConstraint() const {
assert(getKind() == RequirementKind::Layout);
return SecondLayout;
}
/// Whether this requirement is in its canonical form.
bool isCanonical() const;
/// Get the canonical form of this requirement.
Requirement getCanonical() const;
SWIFT_DEBUG_DUMP;
void dump(raw_ostream &out) const;
void print(raw_ostream &os, const PrintOptions &opts) const;
void print(ASTPrinter &printer, const PrintOptions &opts) const;
friend llvm::hash_code hash_value(const Requirement &requirement) {
using llvm::hash_value;
llvm::hash_code first =
hash_value(requirement.FirstTypeAndKind.getOpaqueValue());
llvm::hash_code second;
switch (requirement.getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Superclass:
case RequirementKind::SameType:
second = hash_value(requirement.getSecondType().getPointer());
break;
case RequirementKind::Layout:
second = hash_value(requirement.getLayoutConstraint());
break;
}
return llvm::hash_combine(first, second);
}
friend bool operator==(const Requirement &lhs, const Requirement &rhs) {
if (lhs.FirstTypeAndKind.getOpaqueValue()
!= rhs.FirstTypeAndKind.getOpaqueValue())
return false;
switch (lhs.getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Superclass:
case RequirementKind::SameType:
return lhs.getSecondType().getPointer() ==
rhs.getSecondType().getPointer();
case RequirementKind::Layout:
return lhs.getLayoutConstraint() == rhs.getLayoutConstraint();
}
llvm_unreachable("Unhandled RequirementKind in switch");
}
};
inline void simple_display(llvm::raw_ostream &out, const Requirement &req) {
req.print(out, PrintOptions());
}
} // end namespace swift
#endif