Remove property behaviors

This commit is contained in:
Slava Pestov
2018-12-07 18:07:35 -05:00
parent e160b85f8f
commit aa747dcd81
54 changed files with 31 additions and 2571 deletions

View File

@@ -1337,390 +1337,6 @@ static void synthesizeLazySetterBody(AbstractFunctionDecl *fn, void *context) {
underlyingStorage, ctx);
}
void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD,
VarDecl *BehaviorStorage,
FuncDecl *DefaultInitStorage,
FuncDecl *ParamInitStorage,
Type SelfTy,
Type StorageTy,
NormalProtocolConformance *BehaviorConformance,
SubstitutionMap interfaceMap,
SubstitutionMap contextMap) {
assert(BehaviorStorage);
assert((bool)DefaultInitStorage != (bool)ParamInitStorage);
// Substitute the storage type into the conforming context.
auto SubstStorageInterfaceTy = StorageTy.subst(interfaceMap);
assert(SubstStorageInterfaceTy && "storage type substitution failed?!");
auto SubstStorageContextTy = StorageTy.subst(contextMap);
assert(SubstStorageContextTy && "storage type substitution failed?!");
auto DC = VD->getDeclContext();
SmallString<64> NameBuf = VD->getName().str();
NameBuf += ".storage";
auto StorageName = Context.getIdentifier(NameBuf);
auto storageSpecifier = BehaviorStorage->isSettable(DC)
? VarDecl::Specifier::Var
: VarDecl::Specifier::Let;
auto *Storage = new (Context) VarDecl(
/*IsStatic*/VD->isStatic(), storageSpecifier,
/*IsCaptureList*/false, VD->getLoc(), StorageName,
DC);
Storage->setInterfaceType(SubstStorageInterfaceTy);
Storage->setUserAccessible(false);
// Mark the vardecl to be final, implicit, and private. In a class, this
// prevents it from being dynamically dispatched.
if (VD->getDeclContext()->getSelfClassDecl())
makeFinal(Context, Storage);
Storage->setImplicit();
Storage->setAccess(AccessLevel::Private);
Storage->setSetterAccess(AccessLevel::Private);
addMemberToContextIfNeeded(Storage, DC);
// Initialize the storage immediately, if we can.
Expr *InitStorageExpr = nullptr;
auto Method = DefaultInitStorage ? DefaultInitStorage : ParamInitStorage;
auto SpecializeInitStorage = ConcreteDeclRef(Method, contextMap);
if (DefaultInitStorage ||
(ParamInitStorage && VD->getParentInitializer())) {
// Build the initializer expression, 'Self.initStorage()', using the
// conformance.
auto SelfTypeRef = TypeExpr::createImplicit(SelfTy, Context);
auto InitStorageRef = new (Context) DeclRefExpr(SpecializeInitStorage,
DeclNameLoc(),
/*implicit*/ true);
auto InitStorageMethodTy = FunctionType::get({}, SubstStorageContextTy);
FunctionType::Param SelfParam(SelfTypeRef->getType());
auto InitStorageRefTy = FunctionType::get({SelfParam}, InitStorageMethodTy);
InitStorageRef->setType(InitStorageRefTy);
auto SelfApply = new (Context) DotSyntaxCallExpr(InitStorageRef,
SourceLoc(),
SelfTypeRef);
SelfApply->setImplicit();
SelfApply->setType(InitStorageMethodTy);
SelfApply->setThrows(false);
SmallVector<Expr *, 1> InitStorageArgs;
SmallVector<Identifier, 1> InitStorageArgLabels;
if (ParamInitStorage) {
// Claim the var initializer as the parameter to the `initStorage`
// method.
auto InitValue = VD->getParentInitializer();
auto PBD = VD->getParentPatternBinding();
unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD);
PBD->setInit(entryIndex, nullptr);
PBD->setInitializerChecked(entryIndex);
// Recontextualize any closure declcontexts nested in the initializer to
// realize that they are in the initialization context.
InitValue->walk(RecontextualizeClosures(DC));
// Coerce to the property type.
auto PropertyType =
Type(contextMap.getGenericSignature()->getGenericParams()[1])
.subst(contextMap);
InitValue = new (Context) CoerceExpr(InitValue, SourceLoc(),
TypeLoc::withoutLoc(PropertyType));
// Type-check the expression.
typeCheckExpression(InitValue, DC);
InitStorageArgs.push_back(InitValue);
InitStorageArgLabels.push_back(Identifier());
}
auto InitStorageExpr = CallExpr::createImplicit(Context,SelfApply,
InitStorageArgs,
InitStorageArgLabels);
InitStorageExpr->setType(SubstStorageContextTy);
InitStorageExpr->setThrows(false);
} else {
// Save the storage property and the initStorage reference for later.
// We'll leave it to DI analysis to insert the initializer call at the
// right place.
auto *Behavior = VD->getMutableBehavior();
Behavior->StorageDecl = Storage;
Behavior->InitStorageDecl = SpecializeInitStorage;
}
// Create the pattern binding decl for the storage decl. This will get
// default initialized using the protocol's initStorage() method.
Pattern *PBDPattern = new (Context) NamedPattern(Storage, /*implicit*/true);
PBDPattern = TypedPattern::createImplicit(Context, PBDPattern,
SubstStorageContextTy);
auto *PBD = PatternBindingDecl::createImplicit(
Context, VD->getParentPatternBinding()->getStaticSpelling(), PBDPattern,
InitStorageExpr, VD->getDeclContext(), /*VarLoc*/ VD->getLoc());
PBD->setInitializerChecked(0);
addMemberToContextIfNeeded(PBD, VD->getDeclContext(), VD);
// Add accessors to the storage, since we'll need them to satisfy the
// conformance requirements.
addTrivialAccessorsToStorage(Storage, Context);
// FIXME: Hack to eliminate spurious diagnostics.
if (BehaviorStorage->isStatic() != Storage->isStatic()) return;
// Add the witnesses to the conformance.
recordKnownWitness(BehaviorConformance, BehaviorStorage, Storage);
recordKnownWitness(BehaviorConformance, BehaviorStorage->getGetter(),
Storage->getGetter());
if (BehaviorStorage->isSettable(DC))
recordKnownWitness(BehaviorConformance, BehaviorStorage->getSetter(),
Storage->getSetter());
}
void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD,
FuncDecl *BehaviorParameter,
NormalProtocolConformance *BehaviorConformance,
SubstitutionMap interfaceMap) {
// Create a method to witness the requirement.
auto DC = VD->getDeclContext();
SmallString<64> NameBuf = VD->getName().str();
NameBuf += ".parameter";
auto ParameterBaseName = Context.getIdentifier(NameBuf);
// Substitute the requirement type into the conforming context.
auto ParameterTy = BehaviorParameter->getInterfaceType()
->castTo<AnyFunctionType>()
->getResult();
GenericSignature *genericSig = nullptr;
GenericEnvironment *genericEnv = nullptr;
auto SubstInterfaceTy = ParameterTy.subst(interfaceMap);
assert(SubstInterfaceTy && "storage type substitution failed?!");
auto SubstBodyResultTy = SubstInterfaceTy->castTo<AnyFunctionType>()
->getResult();
// Add the Self type back to the interface and context types.
if (DC->isTypeContext()) {
FunctionType::Param SelfParam(DC->getSelfInterfaceType());
if (DC->isGenericContext()) {
genericSig = DC->getGenericSignatureOfContext();
genericEnv = DC->getGenericEnvironmentOfContext();
SubstInterfaceTy =
GenericFunctionType::get(genericSig, {SelfParam}, SubstInterfaceTy);
} else {
SubstInterfaceTy =
FunctionType::get({SelfParam}, SubstInterfaceTy);
}
}
// Borrow the parameters from the requirement declaration.
SmallVector<ParamDecl *, 4> Params;
SmallVector<Identifier, 4> NameComponents;
auto *DeclaredParams = BehaviorParameter->getParameters();
for (unsigned i : indices(*DeclaredParams)) {
auto declaredParam = DeclaredParams->get(i);
auto declaredParamTy = declaredParam->getInterfaceType();
auto interfaceTy = declaredParamTy.subst(interfaceMap);
assert(interfaceTy);
auto declaredSpecifier = declaredParam->getSpecifier();
SmallString<64> ParamNameBuf;
{
llvm::raw_svector_ostream names(ParamNameBuf);
names << "%arg." << i;
}
auto param = new (Context) ParamDecl(
declaredSpecifier, SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
Context.getIdentifier(ParamNameBuf), DC);
param->setInterfaceType(interfaceTy);
param->setImplicit();
Params.push_back(param);
NameComponents.push_back(Identifier());
}
auto *ParamList = ParameterList::create(Context, Params);
auto *Parameter =
FuncDecl::create(Context, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
/*FuncLoc=*/SourceLoc(),
DeclName(Context, ParameterBaseName, NameComponents),
/*NameLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
/*GenericParams=*/nullptr, ParamList,
TypeLoc::withoutLoc(SubstBodyResultTy), DC);
Parameter->setInterfaceType(SubstInterfaceTy);
Parameter->setGenericEnvironment(genericEnv);
Parameter->setValidationToChecked();
// Mark the method to be final, implicit, and private. In a class, this
// prevents it from being dynamically dispatched.
if (DC->getSelfClassDecl())
makeFinal(Context, Parameter);
Parameter->setImplicit();
Parameter->setAccess(AccessLevel::Private);
// Recontextualize any closure declcontexts nested in the initializer to
// realize that they are in the parameter function.
assert(VD->getBehavior()->Param);
VD->getBehavior()->Param->walk(RecontextualizeClosures(Parameter));
// Apply and return the closure in the function context.
SmallVector<Expr *, 4> argRefs;
SmallVector<Identifier, 4> argNames;
for (unsigned i : indices(Params)) {
auto param = Params[i];
auto expr = new (Context) DeclRefExpr(param, DeclNameLoc(),
/*implicit*/ true);
argRefs.push_back(expr);
argNames.push_back(DeclaredParams->get(i)->getName());
}
auto apply = CallExpr::createImplicit(Context, VD->getBehavior()->Param,
argRefs, argNames);
// Return the expression value.
auto Ret = new (Context) ReturnStmt(SourceLoc(), apply,
/*implicit*/ true);
auto Body = BraceStmt::create(Context, SourceLoc(), ASTNode(Ret),
SourceLoc(), /*implicit*/ true);
Parameter->setBody(Body);
typeCheckDecl(Parameter);
addMemberToContextIfNeeded(Parameter, DC);
// Add the witnesses to the conformance.
recordKnownWitness(BehaviorConformance, BehaviorParameter, Parameter);
}
void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD,
VarDecl *ValueImpl,
Type valueTy,
SubstitutionMap SelfInterfaceSubs,
SubstitutionMap SelfContextSubs) {
auto selfGenericParamTy = Type(GenericTypeParamType::get(0, 0, Context));
auto selfTy = selfGenericParamTy.subst(SelfContextSubs);
auto selfIfaceTy = selfGenericParamTy.subst(SelfInterfaceSubs);
SmallVector<ASTNode, 3> bodyStmts;
auto makeSelfExpr = [&](FuncDecl *fromAccessor,
FuncDecl *toAccessor) -> Expr * {
Expr *selfExpr;
if (VD->getDeclContext()->isTypeContext()) {
ConcreteDeclRef selfRef = fromAccessor->getImplicitSelfDecl();
selfExpr = new (Context) DeclRefExpr(selfRef, DeclNameLoc(),
/*implicit*/ true);
} else {
// self is the empty tuple outside of a type.
selfExpr = TupleExpr::createEmpty(Context, SourceLoc(), SourceLoc(),
/*implicit*/ true);
}
// If forwarding from a nonmutating to a mutating accessor, we need to put
// `self` in a mutable temporary.
auto fromMutating = VD->getDeclContext()->isTypeContext()
&& fromAccessor->getImplicitSelfDecl()->isSettable(fromAccessor);
if (!fromMutating
&& toAccessor->getImplicitSelfDecl()->isSettable(toAccessor)) {
selfExpr->setType(selfTy);
auto var = new (Context) VarDecl(/*IsStatic*/false,
VarDecl::Specifier::Var,
/*IsCaptureList*/false, SourceLoc(),
Context.getIdentifier("tempSelf"),
fromAccessor);
var->setInterfaceType(selfIfaceTy);
var->setImplicit();
auto varPat = new (Context) NamedPattern(var, /*implicit*/ true);
auto *pbd = PatternBindingDecl::createImplicit(
Context, StaticSpellingKind::None, varPat, selfExpr, fromAccessor);
bodyStmts.push_back(var);
bodyStmts.push_back(pbd);
selfExpr = new (Context) DeclRefExpr(var, DeclNameLoc(),
/*implicit*/ true);
}
assert((!fromMutating
|| toAccessor->getImplicitSelfDecl()->isSettable(toAccessor))
&& "can't forward from mutating to nonmutating");
if (!toAccessor->isMutating()) {
selfExpr->setType(selfTy);
} else {
// Access the base as inout if the accessor is mutating.
auto lvTy = LValueType::get(selfTy);
selfExpr->setType(lvTy);
selfExpr = new (Context) InOutExpr(SourceLoc(),
selfExpr, selfTy, /*implicit*/ true);
}
return selfExpr;
};
{
auto getter = VD->getGetter();
assert(getter);
Expr *selfExpr = makeSelfExpr(getter, ValueImpl->getGetter());
auto implRef = ConcreteDeclRef(ValueImpl, SelfContextSubs);
auto implMemberExpr = new (Context) MemberRefExpr(selfExpr,
SourceLoc(),
implRef,
DeclNameLoc(),
/*implicit*/ true);
Expr *returnExpr;
if (ValueImpl->isSettable(VD->getDeclContext())) {
auto valueLVTy = LValueType::get(valueTy);
implMemberExpr->setType(valueLVTy);
returnExpr = new (Context) LoadExpr(implMemberExpr,
valueTy);
returnExpr->setImplicit();
} else {
implMemberExpr->setType(valueTy);
returnExpr = implMemberExpr;
}
auto returnStmt = new (Context) ReturnStmt(SourceLoc(), returnExpr,
/*implicit*/ true);
bodyStmts.push_back(returnStmt);
auto body = BraceStmt::create(Context, SourceLoc(), bodyStmts, SourceLoc(),
/*implicit*/ true);
getter->setBody(body);
getter->setBodyTypeCheckedIfPresent();
}
bodyStmts.clear();
if (auto setter = VD->getSetter()) {
Expr *selfExpr = makeSelfExpr(setter, ValueImpl->getSetter());
auto implRef = ConcreteDeclRef(ValueImpl, SelfContextSubs);
auto implMemberExpr = new (Context) MemberRefExpr(selfExpr,
SourceLoc(),
implRef,
DeclNameLoc(),
/*implicit*/ true);
auto valueLVTy = LValueType::get(valueTy);
implMemberExpr->setType(valueLVTy);
ConcreteDeclRef newValueRef = getFirstParamDecl(setter);
auto newValueExpr = new (Context) DeclRefExpr(newValueRef, DeclNameLoc(),
/*implicit*/ true);
newValueExpr->setType(valueTy);
auto assign = new (Context) AssignExpr(implMemberExpr, SourceLoc(),
newValueExpr, /*implicit*/ true);
assign->setType(TupleType::getEmpty(Context));
bodyStmts.push_back(assign);
auto body = BraceStmt::create(Context, SourceLoc(), bodyStmts, SourceLoc(),
/*implicit*/ true);
setter->setBody(body);
setter->setBodyTypeCheckedIfPresent();
}
}
void TypeChecker::completeLazyVarImplementation(VarDecl *VD) {
assert(VD->getAttrs().hasAttribute<LazyAttr>());
assert(VD->getReadImpl() == ReadImplKind::Get);
@@ -1826,151 +1442,6 @@ void swift::triggerAccessorSynthesis(TypeChecker &TC,
});
}
static void maybeAddAccessorsToBehaviorStorage(TypeChecker &TC, VarDecl *var) {
// If there's already a getter, we're done.
if (var->getGetter())
return;
auto *dc = var->getDeclContext();
assert(!var->getBehavior()->Conformance.hasValue());
// The property should be considered computed by the time we're through.
SWIFT_DEFER {
assert(!var->hasStorage() && "behavior var was not made computed");
};
auto behavior = var->getMutableBehavior();
NormalProtocolConformance *conformance = nullptr;
VarDecl *valueProp = nullptr;
bool mightBeMutating = dc->isTypeContext()
&& !var->isStatic()
&& !dc->getDeclaredInterfaceType()->hasReferenceSemantics();
auto makeBehaviorAccessors = [&]{
AccessorDecl *getter;
AccessorDecl *setter = nullptr;
if (valueProp && valueProp->getGetter()) {
getter = createGetterPrototype(var, TC.Context);
// The getter is mutating if the behavior implementation is, unless
// we're in a class or non-instance context.
if (mightBeMutating && valueProp->isGetterMutating())
getter->setSelfAccessKind(SelfAccessKind::Mutating);
getter->setAccess(var->getFormalAccess());
// Make a setter if the behavior property has one.
if (valueProp->getSetter()) {
setter = createSetterPrototype(var, TC.Context, getter);
if (mightBeMutating && valueProp->isSetterMutating())
setter->setSelfAccessKind(SelfAccessKind::Mutating);
// TODO: max of property and implementation setter visibility?
setter->setAccess(var->getFormalAccess());
}
} else {
// Even if we couldn't find a value property, still make up a stub
// getter and setter, so that subsequent diagnostics make sense for a
// computed-ish property.
getter = createGetterPrototype(var, TC.Context);
getter->setAccess(var->getFormalAccess());
setter = createSetterPrototype(var, TC.Context, getter);
setter->setSelfAccessKind(SelfAccessKind::NonMutating);
setter->setAccess(var->getFormalAccess());
}
SmallVector<AccessorDecl*, 2> accessors;
accessors.push_back(getter);
auto isMutable = StorageIsMutable_t(setter != nullptr);
if (isMutable) accessors.push_back(setter);
var->setAccessors(StorageImplInfo::getComputed(isMutable),
SourceLoc(), accessors, SourceLoc());
// Save the conformance and 'value' decl for later type checking.
behavior->Conformance = conformance;
behavior->ValueDecl = valueProp;
};
// Try to resolve the behavior to a protocol.
auto resolution = TypeResolution::forContextual(dc);
auto behaviorType = resolution.resolveType(behavior->ProtocolName, None);
if (!behaviorType) {
return makeBehaviorAccessors();
}
{
// The type must refer to a protocol.
auto behaviorProtoTy = behaviorType->getAs<ProtocolType>();
if (!behaviorProtoTy) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_not_protocol);
behavior->Conformance = (NormalProtocolConformance*)nullptr;
return makeBehaviorAccessors();
}
auto behaviorProto = behaviorProtoTy->getDecl();
// Validate the behavior protocol and all its extensions so we can do
// name lookup.
TC.validateDecl(behaviorProto);
for (auto ext : behaviorProto->getExtensions()) {
TC.validateExtension(ext);
}
// Look up the behavior protocol's "value" property, or bail if it doesn't
// have one. The property's accessors will decide whether the getter
// is mutating, and whether there's a setter. We'll type-check to make
// sure the property type matches later after validation.
auto lookup = TC.lookupMember(dc, behaviorProtoTy, TC.Context.Id_value);
for (auto found : lookup) {
if (auto foundVar = dyn_cast<VarDecl>(found.getValueDecl())) {
if (valueProp) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_protocol_reqt_ambiguous,
TC.Context.Id_value);
TC.diagnose(valueProp->getLoc(), diag::identifier_declared_here,
TC.Context.Id_value);
TC.diagnose(foundVar->getLoc(), diag::identifier_declared_here,
TC.Context.Id_value);
break;
}
valueProp = foundVar;
}
}
if (!valueProp) {
TC.diagnose(behavior->getLoc(),
diag::property_behavior_protocol_no_value);
return makeBehaviorAccessors();
}
TC.validateDecl(valueProp);
var->setIsGetterMutating(mightBeMutating &&
valueProp->isGetterMutating());
var->setIsSetterMutating(mightBeMutating &&
valueProp->isSetterMutating());
// Set up a conformance to represent the behavior instantiation.
// The conformance will be on the containing 'self' type, or '()' if the
// property is in a non-type context.
Type behaviorSelf;
if (dc->isTypeContext()) {
behaviorSelf = dc->getSelfInterfaceType();
assert(behaviorSelf && "type context doesn't have self type?!");
if (var->isStatic())
behaviorSelf = MetatypeType::get(behaviorSelf);
} else {
behaviorSelf = TC.Context.TheEmptyTupleType;
}
conformance = TC.Context.getBehaviorConformance(behaviorSelf,
behaviorProto,
behavior->getLoc(), var,
ProtocolConformanceState::Checking);
}
return makeBehaviorAccessors();
}
static void maybeAddAccessorsToLazyVariable(VarDecl *var, ASTContext &ctx) {
// If there are already accessors, something is invalid; bail out.
if (!var->getImplInfo().isSimpleStored())
@@ -1997,12 +1468,6 @@ static void maybeAddAccessorsToLazyVariable(VarDecl *var, ASTContext &ctx) {
/// - it synthesizes a setter for get+mutableAddress
void swift::maybeAddAccessorsToStorage(TypeChecker &TC,
AbstractStorageDecl *storage) {
// Introduce accessors for a property with behaviors.
if (storage->hasBehavior()) {
maybeAddAccessorsToBehaviorStorage(TC, cast<VarDecl>(storage));
return;
}
// Lazy properties require special handling.
if (storage->getAttrs().hasAttribute<LazyAttr>()) {
maybeAddAccessorsToLazyVariable(cast<VarDecl>(storage), TC.Context);