mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
"Accessibility" has a different meaning for app developers, so we've already deliberately excised it from our diagnostics in favor of terms like "access control" and "access level". Do the same in the compiler now that we aren't constantly pulling things into the release branch. This commit changes the 'Accessibility' enum to be named 'AccessLevel'.
228 lines
9.0 KiB
C++
228 lines
9.0 KiB
C++
//===--- DerivedConformances.cpp - Derived conformance utilities ----------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TypeChecker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "DerivedConformances.h"
|
|
|
|
using namespace swift;
|
|
using namespace DerivedConformance;
|
|
|
|
ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
|
|
ValueDecl *requirement) {
|
|
// Note: whenever you update this function, also update
|
|
// TypeChecker::deriveProtocolRequirement.
|
|
ASTContext &ctx = nominal->getASTContext();
|
|
auto name = requirement->getFullName();
|
|
|
|
// Local function that retrieves the requirement with the same name as
|
|
// the provided requirement, but within the given known protocol.
|
|
auto getRequirement = [&](KnownProtocolKind kind) -> ValueDecl * {
|
|
// Dig out the protocol.
|
|
auto proto = ctx.getProtocol(kind);
|
|
if (!proto) return nullptr;
|
|
|
|
// Check whether this nominal type derives conformances to the
|
|
if (!nominal->derivesProtocolConformance(proto)) return nullptr;
|
|
|
|
// Retrieve the requirement.
|
|
auto results = proto->lookupDirect(name);
|
|
return results.empty() ? nullptr : results.front();
|
|
};
|
|
|
|
// Properties.
|
|
if (isa<VarDecl>(requirement)) {
|
|
// RawRepresentable.rawValue
|
|
if (name.isSimpleName(ctx.Id_rawValue))
|
|
return getRequirement(KnownProtocolKind::RawRepresentable);
|
|
|
|
// Hashable.hashValue
|
|
if (name.isSimpleName(ctx.Id_hashValue))
|
|
return getRequirement(KnownProtocolKind::Hashable);
|
|
|
|
// _BridgedNSError._nsErrorDomain
|
|
if (name.isSimpleName(ctx.Id_nsErrorDomain))
|
|
return getRequirement(KnownProtocolKind::BridgedNSError);
|
|
|
|
// CodingKey.stringValue
|
|
if (name.isSimpleName(ctx.Id_stringValue))
|
|
return getRequirement(KnownProtocolKind::CodingKey);
|
|
|
|
// CodingKey.intValue
|
|
if (name.isSimpleName(ctx.Id_intValue))
|
|
return getRequirement(KnownProtocolKind::CodingKey);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Functions.
|
|
if (auto func = dyn_cast<FuncDecl>(requirement)) {
|
|
if (func->isOperator() && name.getBaseName() == "==")
|
|
return getRequirement(KnownProtocolKind::Equatable);
|
|
|
|
// Encodable.encode(to: Encoder)
|
|
if (name.isCompoundName() && name.getBaseName() == ctx.Id_encode) {
|
|
auto argumentNames = name.getArgumentNames();
|
|
if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_to)
|
|
return getRequirement(KnownProtocolKind::Encodable);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Initializers.
|
|
if (auto ctor = dyn_cast<ConstructorDecl>(requirement)) {
|
|
auto argumentNames = name.getArgumentNames();
|
|
if (argumentNames.size() == 1) {
|
|
if (argumentNames[0] == ctx.Id_rawValue)
|
|
return getRequirement(KnownProtocolKind::RawRepresentable);
|
|
|
|
// CodingKey.init?(stringValue:), CodingKey.init?(intValue:)
|
|
if (ctor->getFailability() == OTK_Optional &&
|
|
(argumentNames[0] == ctx.Id_stringValue ||
|
|
argumentNames[0] == ctx.Id_intValue))
|
|
return getRequirement(KnownProtocolKind::CodingKey);
|
|
|
|
// Decodable.init(from: Decoder)
|
|
if (argumentNames[0] == ctx.Id_from)
|
|
return getRequirement(KnownProtocolKind::Decodable);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Associated types.
|
|
if (isa<AssociatedTypeDecl>(requirement)) {
|
|
// RawRepresentable.RawValue
|
|
if (name.isSimpleName(ctx.Id_RawValue))
|
|
return getRequirement(KnownProtocolKind::RawRepresentable);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
DeclRefExpr *
|
|
DerivedConformance::createSelfDeclRef(AbstractFunctionDecl *fn) {
|
|
ASTContext &C = fn->getASTContext();
|
|
|
|
auto selfDecl = fn->getImplicitSelfDecl();
|
|
return new (C) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/true);
|
|
}
|
|
|
|
FuncDecl *DerivedConformance::declareDerivedPropertyGetter(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
NominalTypeDecl *typeDecl,
|
|
Type propertyInterfaceType,
|
|
Type propertyContextType,
|
|
bool isStatic,
|
|
bool isFinal) {
|
|
auto &C = tc.Context;
|
|
auto parentDC = cast<DeclContext>(parentDecl);
|
|
auto selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC, isStatic);
|
|
ParameterList *params[] = {
|
|
ParameterList::createWithoutLoc(selfDecl),
|
|
ParameterList::createEmpty(C)
|
|
};
|
|
|
|
FuncDecl *getterDecl =
|
|
FuncDecl::create(C, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
|
|
/*FuncLoc=*/SourceLoc(), DeclName(), /*NameLoc=*/SourceLoc(),
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
/*AccessorKeywordLoc=*/SourceLoc(),
|
|
nullptr, params,
|
|
TypeLoc::withoutLoc(propertyInterfaceType), parentDC);
|
|
getterDecl->setImplicit();
|
|
getterDecl->setStatic(isStatic);
|
|
|
|
// If this is supposed to be a final method, mark it as such.
|
|
assert(isFinal || !parentDC->getAsClassOrClassExtensionContext());
|
|
if (isFinal && parentDC->getAsClassOrClassExtensionContext() &&
|
|
!getterDecl->isFinal())
|
|
getterDecl->getAttrs().add(new (C) FinalAttr(/*IsImplicit=*/true));
|
|
|
|
// Compute the interface type of the getter.
|
|
Type interfaceType = FunctionType::get(TupleType::getEmpty(C),
|
|
propertyInterfaceType);
|
|
auto selfParam = computeSelfParam(getterDecl);
|
|
if (auto sig = parentDC->getGenericSignatureOfContext()) {
|
|
getterDecl->setGenericEnvironment(
|
|
parentDC->getGenericEnvironmentOfContext());
|
|
interfaceType = GenericFunctionType::get(sig, {selfParam},
|
|
interfaceType,
|
|
FunctionType::ExtInfo());
|
|
} else
|
|
interfaceType = FunctionType::get({selfParam}, interfaceType,
|
|
FunctionType::ExtInfo());
|
|
getterDecl->setInterfaceType(interfaceType);
|
|
getterDecl->setAccess(std::max(typeDecl->getFormalAccess(),
|
|
AccessLevel::Internal));
|
|
|
|
// If the enum was not imported, the derived conformance is either from the
|
|
// enum itself or an extension, in which case we will emit the declaration
|
|
// normally.
|
|
if (isa<ClangModuleUnit>(parentDC->getModuleScopeContext()))
|
|
tc.Context.addExternalDecl(getterDecl);
|
|
|
|
return getterDecl;
|
|
}
|
|
|
|
std::pair<VarDecl *, PatternBindingDecl *>
|
|
DerivedConformance::declareDerivedReadOnlyProperty(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
NominalTypeDecl *typeDecl,
|
|
Identifier name,
|
|
Type propertyInterfaceType,
|
|
Type propertyContextType,
|
|
FuncDecl *getterDecl,
|
|
bool isStatic,
|
|
bool isFinal) {
|
|
auto &C = tc.Context;
|
|
auto parentDC = cast<DeclContext>(parentDecl);
|
|
|
|
VarDecl *propDecl = new (C) VarDecl(/*IsStatic*/isStatic, VarDecl::Specifier::Var,
|
|
/*IsCaptureList*/false, SourceLoc(), name,
|
|
propertyContextType, parentDC);
|
|
propDecl->setImplicit();
|
|
propDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr,
|
|
SourceLoc());
|
|
propDecl->setAccess(getterDecl->getFormalAccess());
|
|
propDecl->setInterfaceType(propertyInterfaceType);
|
|
|
|
// If this is supposed to be a final property, mark it as such.
|
|
assert(isFinal || !parentDC->getAsClassOrClassExtensionContext());
|
|
if (isFinal && parentDC->getAsClassOrClassExtensionContext() &&
|
|
!propDecl->isFinal())
|
|
propDecl->getAttrs().add(new (C) FinalAttr(/*IsImplicit=*/true));
|
|
|
|
Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true);
|
|
propPat->setType(propertyContextType);
|
|
propPat = new (C) TypedPattern(propPat,
|
|
TypeLoc::withoutLoc(propertyContextType),
|
|
/*implicit*/ true);
|
|
|
|
auto pbDecl = PatternBindingDecl::create(C, SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
SourceLoc(), propPat, nullptr,
|
|
parentDC);
|
|
pbDecl->setImplicit();
|
|
|
|
return {propDecl, pbDecl};
|
|
}
|