Skip to content

Commit

Permalink
Merge pull request #24797 from slavapestov/more-type-checked-accessors
Browse files Browse the repository at this point in the history
More type checked accessors
  • Loading branch information
slavapestov authored May 15, 2019
2 parents 82928fd + 86f9ca7 commit c141c48
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 425 deletions.
14 changes: 10 additions & 4 deletions include/swift/AST/ProtocolConformanceRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace llvm {

namespace swift {

class ConcreteDeclRef;
class ProtocolConformance;

/// A ProtocolConformanceRef is a handle to a protocol conformance which
Expand Down Expand Up @@ -135,10 +136,15 @@ class ProtocolConformanceRef {
return llvm::hash_value(conformance.Union.getOpaqueValue());
}

static Type
getTypeWitnessByName(Type type,
ProtocolConformanceRef conformance,
Identifier name);
Type getTypeWitnessByName(Type type, Identifier name) const;

/// Find a particular named function witness for a type that conforms to
/// the given protocol.
///
/// \param type The conforming type.
///
/// \param name The name of the requirement.
ConcreteDeclRef getWitnessByName(Type type, DeclName name) const;

/// Determine whether this conformance is canonical.
bool isCanonical() const;
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4225,8 +4225,7 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
*bridgedValueType = type;

// Find the Objective-C class type we bridge to.
return ProtocolConformanceRef::getTypeWitnessByName(
type, *conformance, Id_ObjectiveCType);
return conformance->getTypeWitnessByName(type, Id_ObjectiveCType);
}

// Do we conform to Error?
Expand Down
41 changes: 34 additions & 7 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,14 @@ ProtocolConformanceRef::subst(Type origType,
}

Type
ProtocolConformanceRef::getTypeWitnessByName(Type type,
ProtocolConformanceRef conformance,
Identifier name) {
assert(!conformance.isInvalid());
ProtocolConformanceRef::getTypeWitnessByName(Type type, Identifier name) const {
assert(!isInvalid());

// Find the named requirement.
ProtocolDecl *proto = conformance.getRequirement();
ProtocolDecl *proto = getRequirement();
AssociatedTypeDecl *assocType = nullptr;
auto members = proto->lookupDirect(name);
auto members = proto->lookupDirect(name,
NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions);
for (auto member : members) {
assocType = dyn_cast<AssociatedTypeDecl>(member);
if (assocType)
Expand All @@ -162,7 +161,35 @@ ProtocolConformanceRef::getTypeWitnessByName(Type type,
return nullptr;

return assocType->getDeclaredInterfaceType().subst(
SubstitutionMap::getProtocolSubstitutions(proto, type, conformance));
SubstitutionMap::getProtocolSubstitutions(proto, type, *this));
}

ConcreteDeclRef
ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const {
// Find the named requirement.
auto *proto = getRequirement();
auto results =
proto->lookupDirect(name,
NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions);

ValueDecl *requirement = nullptr;
for (auto *result : results) {
if (isa<ProtocolDecl>(result->getDeclContext()))
requirement = result;
}

if (requirement == nullptr)
return ConcreteDeclRef();

// For a type with dependent conformance, just return the requirement from
// the protocol. There are no protocol conformance tables.
if (!isConcrete()) {
auto subs = SubstitutionMap::getProtocolSubstitutions(proto, type, *this);
return ConcreteDeclRef(requirement, subs);
}

auto *resolver = proto->getASTContext().getLazyResolver();
return getConcrete()->getWitnessDeclRef(requirement, resolver);
}

void *ProtocolConformance::operator new(size_t bytes, ASTContext &context,
Expand Down
7 changes: 3 additions & 4 deletions lib/PrintAsObjC/PrintAsObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,10 +1377,9 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,

// Dig out the Objective-C type.
auto conformance = conformances.front();
Type objcType = ProtocolConformanceRef::getTypeWitnessByName(
nominal->getDeclaredType(),
ProtocolConformanceRef(conformance),
ctx.Id_ObjectiveCType);
Type objcType = ProtocolConformanceRef(conformance).getTypeWitnessByName(
nominal->getDeclaredType(),
ctx.Id_ObjectiveCType);
if (!objcType) return nullptr;

// Dig out the Objective-C class.
Expand Down
5 changes: 2 additions & 3 deletions lib/SIL/Bridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,8 @@ Type TypeConverter::getLoweredCBridgedType(AbstractionPattern pattern,
auto conformance = foreignRepresentation.second;
assert(conformance && "Missing conformance?");
Type bridgedTy =
ProtocolConformanceRef::getTypeWitnessByName(
t, ProtocolConformanceRef(conformance),
M.getASTContext().Id_ObjectiveCType);
ProtocolConformanceRef(conformance).getTypeWitnessByName(
t, M.getASTContext().Id_ObjectiveCType);
assert(bridgedTy && "Missing _ObjectiveCType witness?");
if (purpose == BridgedTypePurpose::ForResult && clangTy)
bridgedTy = OptionalType::get(bridgedTy);
Expand Down
124 changes: 23 additions & 101 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,67 +99,6 @@ SubstitutionMap Solution::computeSubstitutions(
lookupConformanceFn);
}

/// Find a particular named function witness for a type that conforms to
/// the given protocol.
///
/// \param tc The type check we're using.
///
/// \param dc The context in which we need a witness.
///
/// \param type The type whose witness to find.
///
/// \param proto The protocol to which the type conforms.
///
/// \param name The name of the requirement.
///
/// \param diag The diagnostic to emit if the protocol definition doesn't
/// have a requirement with the given name.
///
/// \returns The named witness, or an empty ConcreteDeclRef if no witness
/// could be found.
ConcreteDeclRef findNamedWitnessImpl(
TypeChecker &tc, DeclContext *dc, Type type,
ProtocolDecl *proto, DeclName name,
Diag<> diag,
Optional<ProtocolConformanceRef> conformance = None) {
// Find the named requirement.
ValueDecl *requirement = nullptr;
for (auto member : proto->getMembers()) {
auto d = dyn_cast<ValueDecl>(member);
if (!d || !d->hasName())
continue;

if (d->getFullName().matchesRef(name)) {
requirement = d;
break;
}
}

if (!requirement || requirement->isInvalid()) {
tc.diagnose(proto->getLoc(), diag);
return nullptr;
}

// Find the member used to satisfy the named requirement.
if (!conformance) {
conformance = tc.conformsToProtocol(type, proto, dc,
ConformanceCheckFlags::InExpression);
if (!conformance)
return nullptr;
}

// For a type with dependent conformance, just return the requirement from
// the protocol. There are no protocol conformance tables.
if (!conformance->isConcrete()) {
auto subMap = SubstitutionMap::getProtocolSubstitutions(proto, type,
*conformance);
return ConcreteDeclRef(requirement, subMap);
}

auto concrete = conformance->getConcrete();
return concrete->getWitnessDeclRef(requirement, &tc);
}

static bool shouldAccessStorageDirectly(Expr *base, VarDecl *member,
DeclContext *DC) {
// This only matters for stored properties.
Expand Down Expand Up @@ -2974,10 +2913,8 @@ namespace {

DeclName name(tc.Context, DeclBaseName::createConstructor(),
{ tc.Context.Id_arrayLiteral });

ConcreteDeclRef witness =
findNamedWitnessImpl(tc, dc, arrayTy->getRValueType(), arrayProto,
name, diag::array_protocol_broken, conformance);
conformance->getWitnessByName(arrayTy->getRValueType(), name);
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
return nullptr;
expr->setInitializer(witness);
Expand Down Expand Up @@ -3330,8 +3267,20 @@ namespace {
return nullptr;

// Extract a Bool from the resulting expression.
return solution.convertOptionalToBool(result,
cs.getConstraintLocator(expr));
tc.requireOptionalIntrinsics(expr->getLoc());

// Match the optional value against its `Some` case.
auto &ctx = tc.Context;
auto *someDecl = tc.Context.getOptionalSomeDecl();
auto isSomeExpr = new (tc.Context) EnumIsCaseExpr(result, someDecl);
auto boolDecl = ctx.getBoolDecl();

if (!boolDecl) {
tc.diagnose(SourceLoc(), diag::broken_bool);
}

cs.setType(isSomeExpr, boolDecl ? boolDecl->getDeclaredType() : Type());
return isSomeExpr;
}

return expr;
Expand Down Expand Up @@ -3973,10 +3922,7 @@ namespace {
}

Expr *visitLazyInitializerExpr(LazyInitializerExpr *expr) {
// Since `LazyInitializerExpr` should always have a type set,
// there is no need to do anything here.
assert(cs.getType(expr)->isEqual(expr->getSubExpr()->getType()));
return expr;
llvm_unreachable("Already type-checked");
}

Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
Expand Down Expand Up @@ -6845,10 +6791,8 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal,

// Find the witness that we'll use to initialize the type via a builtin
// literal.
auto witness = findNamedWitnessImpl(
tc, dc, type->getRValueType(), builtinProtocol,
builtinLiteralFuncName, brokenBuiltinProtocolDiag,
*builtinConformance);
auto witness = builtinConformance->getWitnessByName(type->getRValueType(),
builtinLiteralFuncName);
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
return nullptr;

Expand Down Expand Up @@ -6896,10 +6840,8 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal,
}

// Find the witness that we'll use to initialize the literal value.
auto witness = findNamedWitnessImpl(
tc, dc, type->getRValueType(), protocol,
literalFuncName, brokenProtocolDiag,
conformance);
auto witness = conformance->getWitnessByName(type->getRValueType(),
literalFuncName);
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
return nullptr;

Expand Down Expand Up @@ -7743,10 +7685,9 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,

if (auto metaType = type->getAs<AnyMetatypeType>())
type = metaType->getInstanceType();

auto witness = findNamedWitnessImpl(
*this, dc, type->getRValueType(), protocol,
name, brokenProtocolDiag);

// Find the member used to satisfy the named requirement.
auto witness = conformance.getWitnessByName(type, name);
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
return nullptr;

Expand Down Expand Up @@ -7853,22 +7794,3 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
rewriter.finalize(result);
return result;
}

Expr *Solution::convertOptionalToBool(Expr *expr,
ConstraintLocator *locator) const {
auto &cs = getConstraintSystem();
auto &tc = cs.getTypeChecker();
tc.requireOptionalIntrinsics(expr->getLoc());

// Match the optional value against its `Some` case.
auto &ctx = tc.Context;
auto isSomeExpr = new (ctx) EnumIsCaseExpr(expr, ctx.getOptionalSomeDecl());
auto boolDecl = ctx.getBoolDecl();

if (!boolDecl) {
tc.diagnose(SourceLoc(), diag::broken_bool);
}

cs.setType(isSomeExpr, boolDecl ? boolDecl->getDeclaredType() : Type());
return isSomeExpr;
}
23 changes: 11 additions & 12 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1740,8 +1740,8 @@ static Type isRawRepresentable(Type fromType, const ConstraintSystem &CS) {
if (!conformance)
return Type();

Type rawTy = ProtocolConformanceRef::getTypeWitnessByName(
fromType, *conformance, CS.getASTContext().Id_RawValue);
Type rawTy = conformance->getTypeWitnessByName(
fromType, CS.getASTContext().Id_RawValue);
return rawTy;
}

Expand Down Expand Up @@ -2119,9 +2119,9 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
ConformanceCheckFlags::InExpression)) {
Type errorCodeType = CS.getType(expr);
Type errorType =
ProtocolConformanceRef::getTypeWitnessByName(errorCodeType, *conformance,
TC.Context.Id_ErrorType)
->getCanonicalType();
conformance->getTypeWitnessByName(errorCodeType,
TC.Context.Id_ErrorType)
->getCanonicalType();
if (errorType) {
auto diag = diagnose(expr->getLoc(), diag::cannot_throw_error_code,
errorCodeType, errorType);
Expand Down Expand Up @@ -6253,9 +6253,8 @@ bool FailureDiagnosis::visitArrayExpr(ArrayExpr *E) {
= CS.TC.conformsToProtocol(contextualType, ALC, CS.DC,
ConformanceCheckFlags::InExpression)) {
Type contextualElementType =
ProtocolConformanceRef::getTypeWitnessByName(
contextualType, *Conformance,
CS.getASTContext().Id_ArrayLiteralElement)
Conformance->getTypeWitnessByName(
contextualType, CS.getASTContext().Id_ArrayLiteralElement)
->getDesugaredType();

// Type check each of the subexpressions in place, passing down the contextual
Expand Down Expand Up @@ -6343,13 +6342,13 @@ bool FailureDiagnosis::visitDictionaryExpr(DictionaryExpr *E) {
}

contextualKeyType =
ProtocolConformanceRef::getTypeWitnessByName(
contextualType, *Conformance, CS.getASTContext().Id_Key)
Conformance->getTypeWitnessByName(
contextualType, CS.getASTContext().Id_Key)
->getDesugaredType();

contextualValueType =
ProtocolConformanceRef::getTypeWitnessByName(
contextualType, *Conformance, CS.getASTContext().Id_Value)
Conformance->getTypeWitnessByName(
contextualType, CS.getASTContext().Id_Value)
->getDesugaredType();

assert(contextualKeyType && contextualValueType &&
Expand Down
4 changes: 1 addition & 3 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2887,9 +2887,7 @@ namespace {
}

Type visitLazyInitializerExpr(LazyInitializerExpr *expr) {
auto type = expr->getType();
assert(type && "LazyInitializerExpr should always have type set");
return type;
llvm_unreachable("Already type-checked");
}

Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
Expand Down
Loading

0 comments on commit c141c48

Please sign in to comment.