mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Handle a parameter to property behaviors.
For prototyping purposes, we only support a single trailing closure parameter to begin with.
This commit is contained in:
@@ -3609,17 +3609,16 @@ enum class AccessStrategy : unsigned char {
|
|||||||
struct BehaviorRecord {
|
struct BehaviorRecord {
|
||||||
// The behavior name.
|
// The behavior name.
|
||||||
TypeRepr *ProtocolName;
|
TypeRepr *ProtocolName;
|
||||||
// The parameter function, if any.
|
// The parameter expression, if any.
|
||||||
FuncDecl *Param;
|
Expr *Param;
|
||||||
|
|
||||||
Optional<NormalProtocolConformance *> Conformance = None;
|
Optional<NormalProtocolConformance *> Conformance = None;
|
||||||
// The 'value' property from the behavior protocol that provides the property
|
// The 'value' property from the behavior protocol that provides the property
|
||||||
// implementation.
|
// implementation.
|
||||||
VarDecl *ValueDecl = nullptr;
|
VarDecl *ValueDecl = nullptr;
|
||||||
|
|
||||||
|
|
||||||
BehaviorRecord(TypeRepr *ProtocolName,
|
BehaviorRecord(TypeRepr *ProtocolName,
|
||||||
FuncDecl *Param)
|
Expr *Param)
|
||||||
: ProtocolName(ProtocolName), Param(Param)
|
: ProtocolName(ProtocolName), Param(Param)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -3919,7 +3918,7 @@ public:
|
|||||||
void setComputedSetter(FuncDecl *Set);
|
void setComputedSetter(FuncDecl *Set);
|
||||||
|
|
||||||
/// \brief Add a behavior to a property.
|
/// \brief Add a behavior to a property.
|
||||||
void addBehavior(TypeRepr *Type, FuncDecl *Param);
|
void addBehavior(TypeRepr *Type, Expr *Param);
|
||||||
|
|
||||||
/// \brief Set a materializeForSet accessor for this declaration.
|
/// \brief Set a materializeForSet accessor for this declaration.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -256,6 +256,9 @@ ERROR(addressor_with_getter,none,
|
|||||||
ERROR(addressor_with_setter,none,
|
ERROR(addressor_with_setter,none,
|
||||||
"%select{variable|subscript}0 cannot provide both 'address' and "
|
"%select{variable|subscript}0 cannot provide both 'address' and "
|
||||||
"a setter; use an ordinary getter instead", (unsigned))
|
"a setter; use an ordinary getter instead", (unsigned))
|
||||||
|
ERROR(behavior_multiple_vars,none,
|
||||||
|
"applying property behavior with parameter to multiple variables is "
|
||||||
|
"not supported", ())
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
ERROR(decl_expected_module_name,none,
|
ERROR(decl_expected_module_name,none,
|
||||||
|
|||||||
@@ -1564,18 +1564,14 @@ ERROR(property_behavior_value_type_doesnt_match,none,
|
|||||||
(Identifier, Type, Identifier, Type))
|
(Identifier, Type, Identifier, Type))
|
||||||
NOTE(property_behavior_value_decl_here,none,
|
NOTE(property_behavior_value_decl_here,none,
|
||||||
"'value' property declared here", ())
|
"'value' property declared here", ())
|
||||||
ERROR(property_behavior_invalid_initialValue_reqt,none,
|
ERROR(property_behavior_invalid_parameter_reqt,none,
|
||||||
"property behavior %0 'initialValue' requirement must be static, "
|
"property behavior %0 has a 'parameter' requirement that is "
|
||||||
"get-only, and of type 'Self.Value'", (Identifier))
|
"static or generic", (Identifier))
|
||||||
ERROR(property_behavior_requires_initialValue,none,
|
ERROR(property_behavior_requires_parameter,none,
|
||||||
"property behavior %0 requires an initializer in the declaration of %1",
|
"property behavior %0 requires a parameter in the declaration of %1",
|
||||||
(Identifier, Identifier))
|
(Identifier, Identifier))
|
||||||
ERROR(property_behavior_requires_unique_initialValue,none,
|
ERROR(property_behavior_invalid_parameter,none,
|
||||||
"property behavior %0 cannot destructure an initializer expression in a "
|
"parameter expression provided, but property behavior %0 does not "
|
||||||
"compound pattern",
|
|
||||||
(Identifier))
|
|
||||||
ERROR(property_behavior_invalid_initializer,none,
|
|
||||||
"initializer expression provided, but property behavior %0 does not "
|
|
||||||
"use it",
|
"use it",
|
||||||
(Identifier))
|
(Identifier))
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ IDENTIFIER(generate)
|
|||||||
IDENTIFIER(Generator)
|
IDENTIFIER(Generator)
|
||||||
IDENTIFIER(hashValue)
|
IDENTIFIER(hashValue)
|
||||||
IDENTIFIER(init)
|
IDENTIFIER(init)
|
||||||
IDENTIFIER(initialValue)
|
|
||||||
IDENTIFIER(initStorage)
|
IDENTIFIER(initStorage)
|
||||||
IDENTIFIER(load)
|
IDENTIFIER(load)
|
||||||
IDENTIFIER(next)
|
IDENTIFIER(next)
|
||||||
@@ -50,6 +49,7 @@ IDENTIFIER(objectAtIndexedSubscript)
|
|||||||
IDENTIFIER(objectForKeyedSubscript)
|
IDENTIFIER(objectForKeyedSubscript)
|
||||||
IDENTIFIER(ObjectiveC)
|
IDENTIFIER(ObjectiveC)
|
||||||
IDENTIFIER_(OptionalNilComparisonType)
|
IDENTIFIER_(OptionalNilComparisonType)
|
||||||
|
IDENTIFIER(parameter)
|
||||||
IDENTIFIER(Protocol)
|
IDENTIFIER(Protocol)
|
||||||
IDENTIFIER(rawValue)
|
IDENTIFIER(rawValue)
|
||||||
IDENTIFIER(RawValue)
|
IDENTIFIER(RawValue)
|
||||||
|
|||||||
@@ -2872,7 +2872,7 @@ void AbstractStorageDecl::setComputedSetter(FuncDecl *Set) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AbstractStorageDecl::addBehavior(TypeRepr *Type,
|
void AbstractStorageDecl::addBehavior(TypeRepr *Type,
|
||||||
FuncDecl *Param) {
|
Expr *Param) {
|
||||||
assert(BehaviorInfo.getPointer() == nullptr && "already set behavior!");
|
assert(BehaviorInfo.getPointer() == nullptr && "already set behavior!");
|
||||||
auto mem = getASTContext().Allocate(sizeof(BehaviorRecord),
|
auto mem = getASTContext().Allocate(sizeof(BehaviorRecord),
|
||||||
alignof(BehaviorRecord));
|
alignof(BehaviorRecord));
|
||||||
|
|||||||
@@ -3956,14 +3956,64 @@ ParserStatus Parser::parseDeclVar(ParseDeclOptions Flags,
|
|||||||
if (consumeIf(tok::kw___behavior)) {
|
if (consumeIf(tok::kw___behavior)) {
|
||||||
auto type = parseType(diag::expected_behavior_name,
|
auto type = parseType(diag::expected_behavior_name,
|
||||||
/*handle completion*/ true);
|
/*handle completion*/ true);
|
||||||
// TODO: recovery. could scan to next closing bracket
|
|
||||||
if (type.isParseError())
|
if (type.isParseError())
|
||||||
return makeParserError();
|
return makeParserError();
|
||||||
if (type.hasCodeCompletion())
|
if (type.hasCodeCompletion())
|
||||||
return makeParserCodeCompletionStatus();
|
return makeParserCodeCompletionStatus();
|
||||||
|
|
||||||
|
// Parse a following trailing closure argument.
|
||||||
|
// FIXME: Handle generalized parameters.
|
||||||
|
Expr *paramExpr = nullptr;
|
||||||
|
if (Tok.is(tok::l_brace)) {
|
||||||
|
// Record the variables that we're trying to set up. This allows us
|
||||||
|
// to cleanly reject "var x = x" when "x" isn't bound to an enclosing
|
||||||
|
// decl (even though names aren't injected into scope when the parameter
|
||||||
|
// is parsed).
|
||||||
|
SmallVector<VarDecl *, 4> Vars;
|
||||||
|
Vars.append(DisabledVars.begin(), DisabledVars.end());
|
||||||
|
pattern->collectVariables(Vars);
|
||||||
|
|
||||||
|
llvm::SaveAndRestore<decltype(DisabledVars)>
|
||||||
|
RestoreCurVars(DisabledVars, Vars);
|
||||||
|
|
||||||
|
llvm::SaveAndRestore<decltype(DisabledVarReason)>
|
||||||
|
RestoreReason(DisabledVarReason, diag::var_init_self_referential);
|
||||||
|
|
||||||
|
// Set up a decl context for the closure.
|
||||||
|
// This will be recontextualized to a method we synthesize during
|
||||||
|
// type checking.
|
||||||
|
if (!CurDeclContext->isLocalContext() && !topLevelDecl && !initContext)
|
||||||
|
initContext = Context.createPatternBindingContext(CurDeclContext);
|
||||||
|
Optional<ParseFunctionBody> initParser;
|
||||||
|
Optional<ContextChange> topLevelParser;
|
||||||
|
if (topLevelDecl)
|
||||||
|
topLevelParser.emplace(*this, topLevelDecl,
|
||||||
|
&State->getTopLevelContext());
|
||||||
|
if (initContext)
|
||||||
|
initParser.emplace(*this, initContext);
|
||||||
|
|
||||||
|
auto closure = parseExprClosure();
|
||||||
|
usedInitContext = true;
|
||||||
|
if (closure.isParseError())
|
||||||
|
return makeParserError();
|
||||||
|
if (closure.hasCodeCompletion())
|
||||||
|
return makeParserCodeCompletionStatus();
|
||||||
|
paramExpr = closure.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned numVars = 0;
|
||||||
pattern->forEachVariable([&](VarDecl *VD) {
|
pattern->forEachVariable([&](VarDecl *VD) {
|
||||||
VD->addBehavior(type.get(), nullptr);
|
++numVars;
|
||||||
|
// TODO: Support parameter closure with multiple vars. This is tricky
|
||||||
|
// since the behavior's parameter type may be dependent on the
|
||||||
|
// property type, so we'd need to clone the closure expr for each var
|
||||||
|
// to re-type-check it.
|
||||||
|
if (numVars > 1 && paramExpr) {
|
||||||
|
diagnose(paramExpr->getLoc(), diag::behavior_multiple_vars);
|
||||||
|
paramExpr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VD->addBehavior(type.get(), paramExpr);
|
||||||
});
|
});
|
||||||
// If we syntactically match the second decl-var production, with a
|
// If we syntactically match the second decl-var production, with a
|
||||||
// var-get-set clause, parse the var-get-set clause.
|
// var-get-set clause, parse the var-get-set clause.
|
||||||
|
|||||||
@@ -1293,75 +1293,159 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD,
|
|||||||
ConcreteDeclRef(Context, Storage->getSetter(), MemberSubs));
|
ConcreteDeclRef(Context, Storage->getSetter(), MemberSubs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::completePropertyBehaviorInitialValue(VarDecl *VD,
|
void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD,
|
||||||
VarDecl *BehaviorInitialValue,
|
FuncDecl *BehaviorParameter,
|
||||||
NormalProtocolConformance *BehaviorConformance,
|
NormalProtocolConformance *BehaviorConformance,
|
||||||
ArrayRef<Substitution> SelfInterfaceSubs,
|
ArrayRef<Substitution> SelfInterfaceSubs,
|
||||||
ArrayRef<Substitution> SelfContextSubs) {
|
ArrayRef<Substitution> SelfContextSubs) {
|
||||||
// Create a property to witness the requirement.
|
// Create a method to witness the requirement.
|
||||||
auto DC = VD->getDeclContext();
|
auto DC = VD->getDeclContext();
|
||||||
SmallString<64> NameBuf = VD->getName().str();
|
SmallString<64> NameBuf = VD->getName().str();
|
||||||
NameBuf += ".initialValue";
|
NameBuf += ".parameter";
|
||||||
auto InitialValueName = Context.getIdentifier(NameBuf);
|
auto ParameterBaseName = Context.getIdentifier(NameBuf);
|
||||||
// TODO: non-static initialValue
|
|
||||||
assert(BehaviorInitialValue->isStatic());
|
// Substitute the requirement type into the conforming context.
|
||||||
auto *InitialValue = new (Context) VarDecl(/*static*/ true,
|
auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext();
|
||||||
/*let*/ false,
|
auto ParameterTy = BehaviorParameter->getInterfaceType()
|
||||||
VD->getLoc(),
|
->castTo<AnyFunctionType>()
|
||||||
InitialValueName,
|
->getResult();
|
||||||
SelfContextSubs[1].getReplacement(),
|
|
||||||
|
TypeSubstitutionMap interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs);
|
||||||
|
auto SubstInterfaceTy = ParameterTy.subst(VD->getModuleContext(),
|
||||||
|
interfaceMap, SubstOptions());
|
||||||
|
assert(SubstInterfaceTy && "storage type substitution failed?!");
|
||||||
|
|
||||||
|
TypeSubstitutionMap contextMap = sig->getSubstitutionMap(SelfContextSubs);
|
||||||
|
auto SubstContextTy = ParameterTy.subst(VD->getModuleContext(),
|
||||||
|
contextMap, SubstOptions());
|
||||||
|
assert(SubstContextTy && "storage type substitution failed?!");
|
||||||
|
|
||||||
|
auto SubstBodyResultTy = SubstContextTy->castTo<AnyFunctionType>()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
// Add the Self type back to the interface and context types.
|
||||||
|
if (DC->isTypeContext()) {
|
||||||
|
if (DC->isGenericContext()) {
|
||||||
|
auto genericSig = DC->getGenericSignatureOfContext();
|
||||||
|
SubstInterfaceTy = GenericFunctionType::get(genericSig,
|
||||||
|
DC->getSelfInterfaceType(),
|
||||||
|
SubstInterfaceTy,
|
||||||
|
AnyFunctionType::ExtInfo());
|
||||||
|
auto genericParams = DC->getGenericParamsOfContext();
|
||||||
|
SubstContextTy = PolymorphicFunctionType::get(DC->getSelfTypeInContext(),
|
||||||
|
SubstContextTy,
|
||||||
|
genericParams);
|
||||||
|
} else {
|
||||||
|
SubstInterfaceTy = FunctionType::get(DC->getSelfInterfaceType(),
|
||||||
|
SubstInterfaceTy);
|
||||||
|
SubstContextTy = FunctionType::get(DC->getSelfTypeInContext(),
|
||||||
|
SubstContextTy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrow the parameters from the requirement declaration.
|
||||||
|
SmallVector<ParameterList *, 2> ParamLists;
|
||||||
|
if (DC->isTypeContext()) {
|
||||||
|
auto self = new (Context) ParamDecl(/*let*/ true, SourceLoc(), SourceLoc(),
|
||||||
|
Identifier(), SourceLoc(),
|
||||||
|
Context.Id_self,
|
||||||
|
DC->getSelfTypeInContext(), DC);
|
||||||
|
self->setInterfaceType(DC->getSelfInterfaceType());
|
||||||
|
self->setImplicit();
|
||||||
|
|
||||||
|
ParamLists.push_back(ParameterList::create(Context, SourceLoc(),
|
||||||
|
self, SourceLoc()));
|
||||||
|
ParamLists.back()->get(0)->setImplicit();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(BehaviorParameter->getParameterLists().size() == 2);
|
||||||
|
SmallVector<ParamDecl *, 4> Params;
|
||||||
|
SmallVector<Identifier, 4> NameComponents;
|
||||||
|
|
||||||
|
auto *DeclaredParams = BehaviorParameter->getParameterList(1);
|
||||||
|
for (unsigned i : indices(*DeclaredParams)) {
|
||||||
|
auto declaredParam = DeclaredParams->get(i);
|
||||||
|
auto declaredParamTy = declaredParam->getInterfaceType();
|
||||||
|
auto interfaceTy = declaredParamTy.subst(DC->getParentModule(),
|
||||||
|
interfaceMap,
|
||||||
|
SubstOptions());
|
||||||
|
assert(interfaceTy);
|
||||||
|
auto contextTy = declaredParamTy.subst(DC->getParentModule(),
|
||||||
|
contextMap, SubstOptions());
|
||||||
|
assert(contextTy);
|
||||||
|
|
||||||
|
SmallString<64> ParamNameBuf;
|
||||||
|
{
|
||||||
|
llvm::raw_svector_ostream names(ParamNameBuf);
|
||||||
|
names << "%arg." << i;
|
||||||
|
}
|
||||||
|
auto param = new (Context) ParamDecl(/*let*/ true, SourceLoc(), SourceLoc(),
|
||||||
|
Identifier(),
|
||||||
|
SourceLoc(),
|
||||||
|
Context.getIdentifier(ParamNameBuf),
|
||||||
|
contextTy, DC);
|
||||||
|
param->setInterfaceType(interfaceTy);
|
||||||
|
param->setImplicit();
|
||||||
|
Params.push_back(param);
|
||||||
|
NameComponents.push_back(Identifier());
|
||||||
|
}
|
||||||
|
ParamLists.push_back(ParameterList::create(Context, Params));
|
||||||
|
|
||||||
|
auto *Parameter = FuncDecl::create(Context, SourceLoc(),
|
||||||
|
StaticSpellingKind::None,
|
||||||
|
SourceLoc(),
|
||||||
|
DeclName(Context, ParameterBaseName,
|
||||||
|
NameComponents),
|
||||||
|
SourceLoc(), SourceLoc(),
|
||||||
|
SourceLoc(), nullptr, SubstContextTy,
|
||||||
|
ParamLists,
|
||||||
|
TypeLoc::withoutLoc(SubstBodyResultTy),
|
||||||
DC);
|
DC);
|
||||||
InitialValue->setInterfaceType(SelfInterfaceSubs[1].getReplacement());
|
|
||||||
InitialValue->setUserAccessible(false);
|
Parameter->setInterfaceType(SubstInterfaceTy);
|
||||||
// Mark the vardecl to be final, implicit, and private. In a class, this
|
// Mark the method to be final, implicit, and private. In a class, this
|
||||||
// prevents it from being dynamically dispatched.
|
// prevents it from being dynamically dispatched.
|
||||||
if (VD->getDeclContext()->getAsClassOrClassExtensionContext())
|
if (DC->getAsClassOrClassExtensionContext())
|
||||||
makeFinal(Context, InitialValue);
|
makeFinal(Context, Parameter);
|
||||||
InitialValue->setImplicit();
|
Parameter->setImplicit();
|
||||||
InitialValue->setAccessibility(Accessibility::Private);
|
Parameter->setAccessibility(Accessibility::Private);
|
||||||
InitialValue->setIsBeingTypeChecked();
|
Parameter->setIsBeingTypeChecked();
|
||||||
|
Parameter->setBodyResultType(SubstBodyResultTy);
|
||||||
addMemberToContextIfNeeded(InitialValue, DC);
|
|
||||||
|
|
||||||
Pattern *InitialPBDPattern = new (Context) NamedPattern(InitialValue,
|
|
||||||
/*implicit*/true);
|
|
||||||
InitialPBDPattern = new (Context) TypedPattern(InitialPBDPattern,
|
|
||||||
TypeLoc::withoutLoc(SelfContextSubs[1].getReplacement()),
|
|
||||||
/*implicit*/ true);
|
|
||||||
auto *InitialPBD = PatternBindingDecl::create(Context,
|
|
||||||
/*staticloc*/SourceLoc(),
|
|
||||||
StaticSpellingKind::KeywordStatic,
|
|
||||||
VD->getLoc(),
|
|
||||||
InitialPBDPattern, nullptr,
|
|
||||||
VD->getDeclContext());
|
|
||||||
InitialPBD->setImplicit();
|
|
||||||
InitialPBD->setInitializerChecked(0);
|
|
||||||
addMemberToContextIfNeeded(InitialPBD, VD->getDeclContext(), VD);
|
|
||||||
|
|
||||||
// Create a getter.
|
|
||||||
auto Get = createGetterPrototype(InitialValue, *this);
|
|
||||||
addMemberToContextIfNeeded(Get, DC);
|
|
||||||
|
|
||||||
// Take the initializer from the PatternBindingDecl for VD.
|
|
||||||
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
|
// Recontextualize any closure declcontexts nested in the initializer to
|
||||||
// realize that they are in the getter function.
|
// realize that they are in the parameter function.
|
||||||
InitValue->walk(RecontextualizeClosures(Get));
|
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 argTuple = TupleExpr::create(Context, SourceLoc(), argRefs,
|
||||||
|
argNames,
|
||||||
|
{}, SourceLoc(),
|
||||||
|
/*trailing closure*/ false,
|
||||||
|
/*implicit*/ true);
|
||||||
|
auto apply = new (Context) CallExpr(VD->getBehavior()->Param, argTuple,
|
||||||
|
/*implicit*/ true);
|
||||||
|
|
||||||
// Return the expression value.
|
// Return the expression value.
|
||||||
auto Ret = new (Context) ReturnStmt(SourceLoc(), InitValue,
|
auto Ret = new (Context) ReturnStmt(SourceLoc(), apply,
|
||||||
/*implicit*/ true);
|
/*implicit*/ true);
|
||||||
auto Body = BraceStmt::create(Context, SourceLoc(), ASTNode(Ret),
|
auto Body = BraceStmt::create(Context, SourceLoc(), ASTNode(Ret),
|
||||||
SourceLoc(), /*implicit*/ true);
|
SourceLoc(), /*implicit*/ true);
|
||||||
Get->setBody(Body);
|
Parameter->setBody(Body);
|
||||||
|
|
||||||
InitialValue->makeComputed(SourceLoc(), Get, nullptr, nullptr, SourceLoc());
|
Parameter->setIsBeingTypeChecked(false);
|
||||||
InitialValue->setIsBeingTypeChecked(false);
|
typeCheckDecl(Parameter, true);
|
||||||
|
typeCheckDecl(Parameter, false);
|
||||||
|
addMemberToContextIfNeeded(Parameter, DC);
|
||||||
|
|
||||||
// Add the witnesses to the conformance.
|
// Add the witnesses to the conformance.
|
||||||
ArrayRef<Substitution> MemberSubs;
|
ArrayRef<Substitution> MemberSubs;
|
||||||
@@ -1370,10 +1454,8 @@ void TypeChecker::completePropertyBehaviorInitialValue(VarDecl *VD,
|
|||||||
->getForwardingSubstitutions(Context);
|
->getForwardingSubstitutions(Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
BehaviorConformance->setWitness(BehaviorInitialValue,
|
BehaviorConformance->setWitness(BehaviorParameter,
|
||||||
ConcreteDeclRef(Context, InitialValue, MemberSubs));
|
ConcreteDeclRef(Context, Parameter, MemberSubs));
|
||||||
BehaviorConformance->setWitness(BehaviorInitialValue->getGetter(),
|
|
||||||
ConcreteDeclRef(Context, Get, MemberSubs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD,
|
void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD,
|
||||||
|
|||||||
@@ -2641,6 +2641,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
//
|
//
|
||||||
// It'd probably be cleaner to model as `where Self: ...` constraints when
|
// It'd probably be cleaner to model as `where Self: ...` constraints when
|
||||||
// we have those.
|
// we have those.
|
||||||
|
//
|
||||||
|
// TODO: Handle non-protocol requirements ('class', base class, etc.)
|
||||||
for (auto refinedProto : behaviorProto->getInheritedProtocols(&TC)) {
|
for (auto refinedProto : behaviorProto->getInheritedProtocols(&TC)) {
|
||||||
ProtocolConformance *inherited = nullptr;
|
ProtocolConformance *inherited = nullptr;
|
||||||
|
|
||||||
@@ -2772,7 +2774,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
// Now that type witnesses are done, satisfy property and method requirements.
|
// Now that type witnesses are done, satisfy property and method requirements.
|
||||||
conformance->setState(ProtocolConformanceState::Checking);
|
conformance->setState(ProtocolConformanceState::Checking);
|
||||||
|
|
||||||
bool requiresInitialValue = false;
|
bool requiresParameter = false;
|
||||||
for (auto requirementDecl : behaviorProto->getMembers()) {
|
for (auto requirementDecl : behaviorProto->getMembers()) {
|
||||||
auto requirement = dyn_cast<ValueDecl>(requirementDecl);
|
auto requirement = dyn_cast<ValueDecl>(requirementDecl);
|
||||||
if (!requirement)
|
if (!requirement)
|
||||||
@@ -2783,6 +2785,8 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
if (auto varReqt = dyn_cast<VarDecl>(requirement)) {
|
if (auto varReqt = dyn_cast<VarDecl>(requirement)) {
|
||||||
// Match a storage requirement.
|
// Match a storage requirement.
|
||||||
if (varReqt->getName() == TC.Context.Id_storage) {
|
if (varReqt->getName() == TC.Context.Id_storage) {
|
||||||
|
TC.validateDecl(varReqt);
|
||||||
|
|
||||||
auto storageTy = varReqt->getInterfaceType();
|
auto storageTy = varReqt->getInterfaceType();
|
||||||
auto expectedInitStorageTy =
|
auto expectedInitStorageTy =
|
||||||
FunctionType::get(TC.Context.TheEmptyTupleType, storageTy);
|
FunctionType::get(TC.Context.TheEmptyTupleType, storageTy);
|
||||||
@@ -2814,6 +2818,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
TC.diagnose(foundFunc->getLoc(),
|
TC.diagnose(foundFunc->getLoc(),
|
||||||
diag::property_behavior_protocol_reqt_here,
|
diag::property_behavior_protocol_reqt_here,
|
||||||
TC.Context.Id_initStorage);
|
TC.Context.Id_initStorage);
|
||||||
|
conformance->setInvalid();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2828,6 +2833,7 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
for (auto found : lookup)
|
for (auto found : lookup)
|
||||||
TC.diagnose(found.Decl->getLoc(),
|
TC.diagnose(found.Decl->getLoc(),
|
||||||
diag::found_candidate);
|
diag::found_candidate);
|
||||||
|
conformance->setInvalid();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2849,67 +2855,56 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
interfaceSubs,
|
interfaceSubs,
|
||||||
contextSubs);
|
contextSubs);
|
||||||
continue;
|
continue;
|
||||||
// Handle an initialValue requirement.
|
|
||||||
} else if (varReqt->getName() == TC.Context.Id_initialValue) {
|
|
||||||
requiresInitialValue = true;
|
|
||||||
|
|
||||||
// The requirement should be static, get-only, and have the type of the
|
|
||||||
// Value.
|
|
||||||
// TODO: A "deferred" initialization would be an instance requirement.
|
|
||||||
Type valueTy = GenericTypeParamType::get(0, 0, TC.Context);
|
|
||||||
assert(valueReqt && "no Value associated type?!");
|
|
||||||
valueTy = DependentMemberType::get(valueTy, valueReqt, TC.Context);
|
|
||||||
|
|
||||||
if (!varReqt->isStatic()
|
|
||||||
|| varReqt->isSettable(dc)
|
|
||||||
|| !varReqt->getInterfaceType()->isEqual(valueTy)) {
|
|
||||||
TC.diagnose(behavior->getLoc(),
|
|
||||||
diag::property_behavior_invalid_initialValue_reqt,
|
|
||||||
behaviorProto->getName());
|
|
||||||
TC.diagnose(varReqt->getLoc(),
|
|
||||||
diag::property_behavior_protocol_reqt_here,
|
|
||||||
TC.Context.Id_initialValue);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The declaration must have an initializer expression.
|
|
||||||
// FIXME: It should have the initializer to itself too, no 'var (a,b)=x'
|
|
||||||
if (!decl->getParentInitializer()) {
|
|
||||||
TC.diagnose(behavior->getLoc(),
|
|
||||||
diag::property_behavior_requires_initialValue,
|
|
||||||
behaviorProto->getName(),
|
|
||||||
decl->getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isa<NamedPattern>(
|
|
||||||
decl->getParentPattern()->getSemanticsProvidingPattern())) {
|
|
||||||
TC.diagnose(decl->getParentInitializer()->getLoc(),
|
|
||||||
diag::property_behavior_requires_unique_initialValue,
|
|
||||||
behaviorProto->getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support initialValue requirements in non-type contexts.
|
|
||||||
if (!dc->isTypeContext()) {
|
|
||||||
TC.diagnose(behavior->getLoc(),
|
|
||||||
diag::property_behavior_with_feature_not_supported,
|
|
||||||
behaviorProto->getName(), "initial value requirement");
|
|
||||||
conformance->setInvalid();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap the initializer expression in a get-only property to
|
|
||||||
TC.completePropertyBehaviorInitialValue(decl, varReqt,
|
|
||||||
conformance,
|
|
||||||
interfaceSubs,
|
|
||||||
contextSubs);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
} else if (auto func = dyn_cast<FuncDecl>(requirement)) {
|
} else if (auto func = dyn_cast<FuncDecl>(requirement)) {
|
||||||
// Handle accessors as part of their property.
|
// Handle accessors as part of their property.
|
||||||
if (func->isAccessor())
|
if (func->isAccessor())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Handle a parameter block requirement.
|
||||||
|
if (func->getName() == TC.Context.Id_parameter) {
|
||||||
|
requiresParameter = true;
|
||||||
|
|
||||||
|
TC.validateDecl(func);
|
||||||
|
|
||||||
|
// The requirement should be for a nongeneric instance method.
|
||||||
|
if (func->isStatic() || func->isGeneric()) {
|
||||||
|
TC.diagnose(behavior->getLoc(),
|
||||||
|
diag::property_behavior_invalid_parameter_reqt,
|
||||||
|
behaviorProto->getName());
|
||||||
|
TC.diagnose(varReqt->getLoc(),
|
||||||
|
diag::property_behavior_protocol_reqt_here,
|
||||||
|
TC.Context.Id_parameter);
|
||||||
|
conformance->setInvalid();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The declaration must have a parameter.
|
||||||
|
if (!decl->getBehavior()->Param) {
|
||||||
|
TC.diagnose(behavior->getLoc(),
|
||||||
|
diag::property_behavior_requires_parameter,
|
||||||
|
behaviorProto->getName(),
|
||||||
|
decl->getName());
|
||||||
|
conformance->setInvalid();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support parameter requirements in non-type contexts.
|
||||||
|
if (!dc->isTypeContext()) {
|
||||||
|
TC.diagnose(behavior->getLoc(),
|
||||||
|
diag::property_behavior_with_feature_not_supported,
|
||||||
|
behaviorProto->getName(), "parameter requirement");
|
||||||
|
conformance->setInvalid();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the initializer expression in a get-only property to
|
||||||
|
TC.completePropertyBehaviorParameter(decl, func,
|
||||||
|
conformance,
|
||||||
|
interfaceSubs,
|
||||||
|
contextSubs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unknownRequirement(requirement);
|
unknownRequirement(requirement);
|
||||||
@@ -2919,9 +2914,9 @@ static void checkVarBehavior(VarDecl *decl, TypeChecker &TC) {
|
|||||||
// use it, complain.
|
// use it, complain.
|
||||||
// TODO: The initializer could eventually be consumed by DI-style
|
// TODO: The initializer could eventually be consumed by DI-style
|
||||||
// initialization.
|
// initialization.
|
||||||
if (!requiresInitialValue && decl->getParentInitializer()) {
|
if (!requiresParameter && decl->getBehavior()->Param) {
|
||||||
TC.diagnose(decl->getParentInitializer()->getLoc(),
|
TC.diagnose(decl->getBehavior()->Param->getLoc(),
|
||||||
diag::property_behavior_invalid_initializer,
|
diag::property_behavior_invalid_parameter,
|
||||||
behaviorProto->getName());
|
behaviorProto->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1031,10 +1031,10 @@ public:
|
|||||||
ArrayRef<Substitution> SelfInterfaceSubs,
|
ArrayRef<Substitution> SelfInterfaceSubs,
|
||||||
ArrayRef<Substitution> SelfContextSubs);
|
ArrayRef<Substitution> SelfContextSubs);
|
||||||
|
|
||||||
/// Instantiate the initial value implementation for a behavior-backed
|
/// Instantiate the parameter implementation for a behavior-backed
|
||||||
/// property.
|
/// property.
|
||||||
void completePropertyBehaviorInitialValue(VarDecl *VD,
|
void completePropertyBehaviorParameter(VarDecl *VD,
|
||||||
VarDecl *BehaviorInitialValue,
|
FuncDecl *BehaviorParameter,
|
||||||
NormalProtocolConformance *BehaviorConformance,
|
NormalProtocolConformance *BehaviorConformance,
|
||||||
ArrayRef<Substitution> SelfInterfaceSubs,
|
ArrayRef<Substitution> SelfInterfaceSubs,
|
||||||
ArrayRef<Substitution> SelfContextSubs);
|
ArrayRef<Substitution> SelfContextSubs);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ extension delayedImmutable {
|
|||||||
protocol lazy {
|
protocol lazy {
|
||||||
associatedtype Value
|
associatedtype Value
|
||||||
var storage: Value? { get set }
|
var storage: Value? { get set }
|
||||||
static var initialValue: Value { get }
|
func parameter() -> Value
|
||||||
}
|
}
|
||||||
extension lazy {
|
extension lazy {
|
||||||
var value: Value {
|
var value: Value {
|
||||||
@@ -46,7 +46,7 @@ extension lazy {
|
|||||||
if let existing = storage {
|
if let existing = storage {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
let value = Self.initialValue
|
let value = parameter()
|
||||||
storage = value
|
storage = value
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ func evaluateLazy() -> Int {
|
|||||||
|
|
||||||
class Foo {
|
class Foo {
|
||||||
var x: Int __behavior delayedImmutable
|
var x: Int __behavior delayedImmutable
|
||||||
var y = evaluateLazy() __behavior lazy
|
var y: Int __behavior lazy { evaluateLazy() }
|
||||||
}
|
}
|
||||||
|
|
||||||
var DelayedImmutable = TestSuite("DelayedImmutable")
|
var DelayedImmutable = TestSuite("DelayedImmutable")
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func exerciseStorage<T>(inout _ sx: S2<T>, inout _ sy: S2<Int>,
|
|||||||
protocol withInit {
|
protocol withInit {
|
||||||
associatedtype Value
|
associatedtype Value
|
||||||
var storage: Value? { get set }
|
var storage: Value? { get set }
|
||||||
static var initialValue: Value { get }
|
func parameter() -> Value
|
||||||
}
|
}
|
||||||
extension withInit {
|
extension withInit {
|
||||||
var value: Value {
|
var value: Value {
|
||||||
@@ -131,14 +131,14 @@ extension withInit {
|
|||||||
static func initStorage() -> Value? { }
|
static func initStorage() -> Value? { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: initialValue behaviors in non-instance context
|
// TODO: parameterized behaviors in non-instance context
|
||||||
func any<T>() -> T { }
|
func any<T>() -> T { }
|
||||||
|
|
||||||
struct S3<T> {
|
struct S3<T> {
|
||||||
var instance: T = any() __behavior withInit
|
var instance: T __behavior withInit { any() }
|
||||||
}
|
}
|
||||||
class C3<T> {
|
class C3<T> {
|
||||||
var instance: T = any() __behavior withInit
|
var instance: T __behavior withInit { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
func exerciseStorage<T>(inout _ sx: S3<T>, inout _ sy: S3<Int>,
|
func exerciseStorage<T>(inout _ sx: S3<T>, inout _ sy: S3<Int>,
|
||||||
|
|||||||
@@ -184,44 +184,37 @@ struct S<T> {
|
|||||||
static var testGetset: T __behavior getset
|
static var testGetset: T __behavior getset
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol initialValue {
|
protocol parameterized {
|
||||||
associatedtype Value
|
associatedtype Value
|
||||||
static var initialValue: Value { get }
|
func parameter() -> Value
|
||||||
}
|
}
|
||||||
extension initialValue {
|
extension parameterized {
|
||||||
var value: Value {
|
var value: Value {
|
||||||
get { }
|
get { }
|
||||||
set { }
|
set { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let compoundInitialValue = (0,0)
|
struct TestParameters {
|
||||||
|
var hasParameter: Int __behavior parameterized { 0 }
|
||||||
struct TestInitialValues {
|
var (sharedParameter1, sharedParameter2): (Int,Int)
|
||||||
var hasInitialValue: Int = 0 __behavior initialValue
|
__behavior parameterized { 0 } // expected-error {{multiple variables is not supported}} expected-error{{sharedParameter2}}
|
||||||
var (sharedInitialValue1, sharedInitialValue2): (Int,Int)
|
var missingParameter: Int __behavior parameterized // expected-error{{requires a parameter}}
|
||||||
= compoundInitialValue __behavior initialValue // expected-error * {{cannot destructure}}
|
var invalidParameter: Int __behavior parameterized { 5.5 } // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}}
|
||||||
var missingInitialValue: Int __behavior initialValue // expected-error{{requires an initializer}}
|
|
||||||
// TODO: "return expression" message is wrong
|
|
||||||
var invalidInitialValue: Int = 5.5 __behavior initialValue // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
var globalInitialValue: Int = 0 __behavior initialValue // expected-error{{not supported}}
|
var globalParameter: Int __behavior parameterized { 0 } // expected-error{{not supported}}
|
||||||
|
|
||||||
protocol noInitialValue {
|
protocol noParameter {
|
||||||
associatedtype Value
|
associatedtype Value
|
||||||
}
|
}
|
||||||
extension noInitialValue {
|
extension noParameter {
|
||||||
var value: Value {
|
var value: Value {
|
||||||
get { }
|
get { }
|
||||||
set { }
|
set { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasNoInitialValue: Int __behavior noInitialValue
|
var hasNoParameter: Int __behavior noParameter
|
||||||
var hasUnwantedInitialValue: Int = 0 __behavior noInitialValue // expected-error{{initializer expression provided, but property behavior 'noInitialValue' does not use it}}
|
var hasUnwantedParameter: Int __behavior noParameter { 0 } // expected-error{{parameter expression provided, but property behavior 'noParameter' does not use it}}
|
||||||
var (hasUnwantedSharedInitialValue1,
|
|
||||||
hasUnwantedSharedInitialValue2): (Int, Int)
|
|
||||||
= compoundInitialValue // expected-error * {{initializer expression provided, but property behavior 'noInitialValue' does not use it}}
|
|
||||||
__behavior noInitialValue
|
|
||||||
|
|||||||
Reference in New Issue
Block a user