blob: 0e914ebaac2826deebd198f027f17c7444860b70 [file] [log] [blame]
//===--- TypeDifferenceVisitor.h - Visitor for pairs of types ---*- 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 TypeDifferenceVisitor, a visitor which finds
// differences between canonical types.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_TYPEDIFFERENCEVISITOR_H
#define SWIFT_AST_TYPEDIFFERENCEVISITOR_H
#include "swift/AST/SILLayout.h"
#include "swift/AST/Types.h"
namespace swift {
// TODO: maybe have a version of this that works on non-canonical types
template <class Impl, class RetTy, class... Args>
class CanTypePairVisitor {
public:
// Provide default implementations that chain to the base class.
#define ABSTRACT_TYPE(CLASS, PARENT) \
RetTy visit##CLASS##Type(Can##CLASS##Type type1, \
Can##CLASS##Type type2, \
Args... args) { \
return static_cast<Impl&>(*this) \
.visit##PARENT(type1, type2, std::forward<Args>(args)...); \
}
#define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT)
#define ABSTRACT_SUGARED_TYPE(CLASS, PARENT)
#define SUGARED_TYPE(CLASS, PARENT)
// Don't allow unchecked types by default, but allow visitors to opt-in to
// handling them.
#define UNCHECKED_TYPE(CLASS, PARENT) \
RetTy visit##CLASS##Type(Can##CLASS##Type type1, \
Can##CLASS##Type type2, \
Args... args) { \
llvm_unreachable("unchecked type"); \
}
#include "swift/AST/TypeNodes.def"
};
/// A CRTP class for finding differences between types.
///
/// The visitors all short-circuit as soon as one returns true.
///
/// visitDifferentTypes()
template <class Impl>
class CanTypeDifferenceVisitor : public CanTypePairVisitor<Impl, bool> {
protected:
Impl &asImpl() { return static_cast<Impl&>(*this); }
public:
/// Two component types differ.
bool visitDifferentComponentTypes(CanType type1, CanType type2) {
asImpl().visitDifferentTypes(type1, type2);
// Short-circuit by default.
return true;
}
/// Two types differ in non-type structure, like a convention or a label.
/// Generally, you can't usefully recover when this is called; it always
/// needs to return true.
bool visitDifferentTypeStructure(CanType type1, CanType type2) {
asImpl().visitDifferentTypes(type1, type2);
return true;
}
/// Inform the subclass that a difference was detected.
void visitDifferentTypes(CanType type1, CanType type2) {}
bool visit(CanType type1, CanType type2) {
if (type1 == type2)
return false;
if (type1->getKind() != type2->getKind())
return asImpl().visitDifferentComponentTypes(type1, type2);
switch (type1->getKind()) {
#define SUGARED_TYPE(CLASS, PARENT) \
case TypeKind::CLASS:
#define TYPE(CLASS, PARENT)
#include "swift/AST/TypeNodes.def"
llvm_unreachable("non-canonical type");
#define SUGARED_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) \
case TypeKind::CLASS: \
return asImpl().visit##CLASS##Type(cast<CLASS##Type>(type1), \
cast<CLASS##Type>(type2));
#include "swift/AST/TypeNodes.def"
}
llvm_unreachable("Not reachable, all cases handled");
}
// In the type-specific visitors, we know that we have
// non-identical types.
// These types are singleton and can't actually differ.
#define SINGLETON_TYPE(SHORT_ID, ID) \
bool visit##ID##Type(Can##ID##Type type1, Can##ID##Type type2) {\
llvm_unreachable("singleton type that wasn't identical"); \
}
#include "swift/AST/TypeNodes.def"
bool visitBuiltinIntegerType(CanBuiltinIntegerType type1,
CanBuiltinIntegerType type2) {
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitBuiltinFloatType(CanBuiltinFloatType type1,
CanBuiltinFloatType type2) {
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitBuiltinVectorType(CanBuiltinVectorType type1,
CanBuiltinVectorType type2) {
if (type1->getNumElements() != type2->getNumElements())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(type1.getElementType(), type2.getElementType());
}
bool visitTupleType(CanTupleType type1, CanTupleType type2) {
return visitComponentArray(type1, type2,
type1->getElements(), type2->getElements());
}
bool visitComponent(CanType type1, CanType type2,
const TupleTypeElt &elt1, const TupleTypeElt &elt2) {
if (elt1.getName() != elt2.getName())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(CanType(elt1.getType()), CanType(elt2.getType()));
}
bool visitReferenceStorageType(CanReferenceStorageType type1,
CanReferenceStorageType type2) {
return asImpl().visit(type1.getReferentType(), type2.getReferentType());
}
bool visitUnboundGenericType(CanUnboundGenericType type1,
CanUnboundGenericType type2) {
assert(type1->getDecl() != type2->getDecl());
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitNominalType(CanNominalType type1, CanNominalType type2) {
assert(type1->getDecl() != type2->getDecl());
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitBoundGenericType(CanBoundGenericType type1,
CanBoundGenericType type2) {
if (type1->getDecl() != type2->getDecl())
return asImpl().visitDifferentTypeStructure(type1, type2);
return visitComponentArray(type1, type2,
type1.getGenericArgs(), type2.getGenericArgs());
}
bool visitAnyMetatypeType(CanAnyMetatypeType type1,
CanAnyMetatypeType type2) {
if (type1->hasRepresentation() != type2->hasRepresentation() ||
(type1->hasRepresentation() &&
type1->getRepresentation() != type2->getRepresentation()))
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(type1.getInstanceType(), type2.getInstanceType());
}
bool visitModuleType(CanModuleType type1, CanModuleType type2) {
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitDynamicSelfType(CanDynamicSelfType type1,
CanDynamicSelfType type2) {
return asImpl().visit(type1.getSelfType(), type2.getSelfType());
}
bool visitSubstitutableType(CanSubstitutableType type1,
CanSubstitutableType type2) {
return asImpl().visitDifferentComponentTypes(type1, type2);
}
bool visitDependentMemberType(CanDependentMemberType type1,
CanDependentMemberType type2) {
if (type1->getName() != type2->getName())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(type1.getBase(), type2.getBase());
}
bool visitGenericFunctionType(CanGenericFunctionType type1,
CanGenericFunctionType type2) {
if (type1.getGenericSignature() != type2.getGenericSignature())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visitAnyFunctionType(type1, type2);
}
bool visitAnyFunctionType(CanAnyFunctionType type1,
CanAnyFunctionType type2) {
if (!type1->hasSameExtInfoAs(type2))
return asImpl().visitDifferentTypeStructure(type1, type2);
if (asImpl().visit(type1.getResult(), type2.getResult()))
return true;
return visitComponentArray(type1, type2,
type1.getParams(), type2.getParams());
}
bool visitComponent(CanType type1, CanType type2,
AnyFunctionType::CanParam param1,
AnyFunctionType::CanParam param2) {
if (param1.getLabel() != param2.getLabel() ||
param1.getParameterFlags() != param2.getParameterFlags())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(param1.getPlainType(), param2.getPlainType());
}
bool visitSILFunctionType(CanSILFunctionType type1,
CanSILFunctionType type2) {
return (asImpl().visitSILFunctionTypeStructure(type1, type2) ||
asImpl().visitSILFunctionTypeSubstitutions(type1, type2) ||
asImpl().visitSILFunctionTypeComponents(type1, type2));
}
bool visitSILFunctionTypeStructure(CanSILFunctionType type1,
CanSILFunctionType type2) {
if (!type1->hasSameExtInfoAs(type2) ||
type1->getCoroutineKind() != type2->getCoroutineKind() ||
type1->getInvocationGenericSignature() !=
type2->getInvocationGenericSignature())
return asImpl().visitDifferentTypeStructure(type1, type2);
return false;
}
bool visitSILFunctionTypeSubstitutions(CanSILFunctionType type1,
CanSILFunctionType type2) {
return asImpl().visitOptSubstitutionMap(type1, type2,
type1->getPatternSubstitutions(),
type2->getPatternSubstitutions())
|| asImpl().visitOptSubstitutionMap(type1, type2,
type1->getInvocationSubstitutions(),
type2->getInvocationSubstitutions());
}
bool visitSILFunctionTypeComponents(CanSILFunctionType type1,
CanSILFunctionType type2) {
return visitComponentArray(type1, type2,
type1->getParameters(), type2->getParameters())
|| visitComponentArray(type1, type2,
type1->getResults(), type2->getResults())
|| visitComponentArray(type1, type2,
type1->getYields(), type2->getYields());
}
bool visitComponent(CanType type1, CanType type2,
SILParameterInfo param1, SILParameterInfo param2) {
if (param1.getConvention() != param2.getConvention() ||
param1.getDifferentiability() != param2.getDifferentiability())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(param1.getInterfaceType(),
param2.getInterfaceType());
}
bool visitComponent(CanType type1, CanType type2,
SILResultInfo result1, SILResultInfo result2) {
if (result1.getConvention() != result2.getConvention())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(result1.getInterfaceType(),
result2.getInterfaceType());
}
bool visitComponent(CanType type1, CanType type2,
SILYieldInfo yield1, SILYieldInfo yield2) {
if (yield1.getConvention() != yield2.getConvention())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(yield1.getInterfaceType(),
yield2.getInterfaceType());
}
bool visitSILBoxType(CanSILBoxType type1, CanSILBoxType type2) {
return (asImpl().visitSILLayout(type1, type2,
type1->getLayout(), type2->getLayout()) ||
asImpl().visitOptSubstitutionMap(type1, type2,
type1->getSubstitutions(),
type2->getSubstitutions()));
}
bool visitSILLayout(CanType type1, CanType type2,
SILLayout *layout1, SILLayout *layout2) {
if (layout1->getGenericSignature() != layout2->getGenericSignature() ||
layout1->isMutable() != layout2->isMutable())
return asImpl().visitDifferentTypeStructure(type1, type2);
return visitComponentArray(type1, type2,
layout1->getFields(), layout2->getFields());
}
bool visitComponent(CanType type1, CanType type2,
const SILField &field1, const SILField &field2) {
if (field1.isMutable() != field2.isMutable())
return asImpl().visitDifferentTypeStructure(type1, type2);
return asImpl().visit(field1.getLoweredType(), field2.getLoweredType());
}
bool visitSILBlockStorageType(CanSILBlockStorageType type1,
CanSILBlockStorageType type2) {
return asImpl().visit(type1->getCaptureType(), type2->getCaptureType());
}
bool visitProtocolCompositionType(CanProtocolCompositionType type1,
CanProtocolCompositionType type2) {
return visitComponentArray(type1, type2,
type1->getMembers(), type2->getMembers());
}
bool visitLValueType(CanLValueType type1, CanLValueType type2) {
return asImpl().visit(type1.getObjectType(), type2.getObjectType());
}
bool visitInOutType(CanInOutType type1, CanInOutType type2) {
return asImpl().visit(type1.getObjectType(), type2.getObjectType());
}
bool visitErrorType(CanErrorType type1, CanErrorType type2) {
return false;
}
bool visitOptSubstitutionMap(CanType type1, CanType type2,
SubstitutionMap subs1, SubstitutionMap subs2) {
if ((bool) subs1 != (bool) subs2)
return asImpl().visitDifferentTypeStructure(type1, type2);
if (subs1)
return asImpl().visitSubstitutionMap(type1, type2, subs1, subs2);
return false;
}
bool visitSubstitutionMap(CanType type1, CanType type2,
SubstitutionMap subs1, SubstitutionMap subs2) {
if (CanGenericSignature(subs1.getGenericSignature())
!= CanGenericSignature(subs2.getGenericSignature()))
return asImpl().visitDifferentTypeStructure(type1, type2);
return visitComponentArray(type1, type2,
subs1.getReplacementTypes(),
subs2.getReplacementTypes());
}
private:
bool visitComponent(CanType type1, CanType type2,
Type componentType1, Type componentType2) {
return asImpl().visit(CanType(componentType1), CanType(componentType2));
}
template <class T>
bool visitComponentArray(CanType type1, CanType type2, T array1, T array2) {
if (array1.size() != array2.size())
return asImpl().visitDifferentTypeStructure(type1, type2);
for (auto i : indices(array1)) {
if (asImpl().visitComponent(type1, type2, array1[i], array2[i]))
return true;
}
return false;
}
};
}
#endif