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:
Joe Groff
2016-02-29 11:28:34 -08:00
parent 2da687b6e4
commit a4fa786a6f
12 changed files with 286 additions and 168 deletions

View File

@@ -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.
/// ///

View File

@@ -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,

View File

@@ -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))

View File

@@ -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)

View File

@@ -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));

View File

@@ -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.

View File

@@ -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,

View File

@@ -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());
} }

View File

@@ -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);

View File

@@ -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")

View File

@@ -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>,

View File

@@ -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