Skip to content

More type checked accessors #24797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 15, 2019
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