Skip to content

Commit aa4e732

Browse files
committed
Support placeholder types
1 parent 05f7fef commit aa4e732

2 files changed

Lines changed: 208 additions & 14 deletions

File tree

lib/Sema/TypeCheckType.cpp

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6984,14 +6984,65 @@ struct ResultBuilderUnboundTypeOpener {
69846984
CustomAttr *attr;
69856985

69866986
Type operator()(UnboundGenericType *unboundTy) const {
6987+
return open(unboundTy, /*genericArgs=*/{});
6988+
}
6989+
6990+
// Opens a partially-bound result builder type by evaluating any
6991+
// placeholder types in the generic signature
6992+
Type openPlaceholders(Type type) const {
6993+
auto &ctx = dc->getASTContext();
6994+
6995+
if (!type->hasPlaceholder()) {
6996+
return type;
6997+
}
6998+
6999+
// @Builder<_>: placeholder is in the type's own generic args
7000+
if (auto *boundGeneric = type->getAs<BoundGenericType>()) {
7001+
if (auto *nominalDecl =
7002+
dyn_cast<NominalTypeDecl>(boundGeneric->getDecl())) {
7003+
auto *unboundTy = UnboundGenericType::get(
7004+
nominalDecl, boundGeneric->getParent(), ctx);
7005+
return open(unboundTy, boundGeneric->getGenericArgs());
7006+
}
7007+
}
7008+
7009+
// @Array<_>.Builder: placeholder is in the parent type
7010+
else if (auto *nominalType = type->getAs<NominalType>()) {
7011+
if (auto *parentBoundGeneric =
7012+
nominalType->getParent()->getAs<BoundGenericType>()) {
7013+
if (auto *parentNominalDecl =
7014+
dyn_cast<NominalTypeDecl>(parentBoundGeneric->getDecl())) {
7015+
auto *unboundParent = UnboundGenericType::get(
7016+
parentNominalDecl, parentBoundGeneric->getParent(), ctx);
7017+
auto solvedParent =
7018+
open(unboundParent, parentBoundGeneric->getGenericArgs());
7019+
7020+
if (solvedParent->hasError()) {
7021+
return solvedParent;
7022+
}
7023+
7024+
return NominalType::get(nominalType->getDecl(), solvedParent, ctx);
7025+
}
7026+
}
7027+
}
7028+
return type;
7029+
}
7030+
7031+
/// Opens the given result builder type by solving its generic parameters
7032+
/// against the owning declaration's return type.
7033+
///
7034+
/// If `genericArgs` is provided, any non-placeholder type remains
7035+
/// constrained to that type, and only the placeholders are solved.
7036+
Type open(UnboundGenericType *unboundTy,
7037+
ArrayRef<Type> genericArgs = {}) const {
69877038
auto resultBuilderDecl = attr->getNominalDecl();
69887039
if (!resultBuilderDecl) {
69897040
return invalidResultBuilderType();
69907041
}
69917042

69927043
auto unboundTyDecl =
69937044
dyn_cast_or_null<NominalTypeDecl>(unboundTy->getDecl());
6994-
if (!resultBuilderDecl) {
7045+
if (!unboundTyDecl) {
69957046
return invalidResultBuilderType();
69967047
}
69977048

@@ -7019,19 +7070,37 @@ struct ResultBuilderUnboundTypeOpener {
70197070

70207071
ConstraintSystem cs(dc, options);
70217072

7022-
// Create a type variable for each of the result builder's generic params
7073+
// For each generic parameter position, either pin the fixed argument
7074+
// (when genericArgs supplies a non-placeholder at that index) or create a
7075+
// type variable to be solved.
70237076
llvm::SmallVector<Type, 8> typeVarReplacements;
7024-
llvm::SmallVector<TypeVariableType *, 8> typeVars;
70257077
for (unsigned i = 0; i < genericSig.getGenericParams().size(); ++i) {
7026-
auto locator = cs.getConstraintLocator(
7027-
unboundTyDecl, {ConstraintLocator::GenericArgument, i});
7028-
auto typeVar = cs.createTypeVariable(locator, TVO_CanBindToHole);
7029-
typeVarReplacements.push_back(typeVar);
7030-
typeVars.push_back(typeVar);
7078+
if (i < genericArgs.size() && !genericArgs[i]->hasPlaceholder()) {
7079+
typeVarReplacements.push_back(genericArgs[i]);
7080+
} else {
7081+
auto locator = cs.getConstraintLocator(
7082+
unboundTyDecl, {ConstraintLocator::GenericArgument, i});
7083+
auto makeTypeVar = [&] {
7084+
return Type(cs.createTypeVariable(locator, TVO_CanBindToHole));
7085+
};
7086+
7087+
if (i >= genericArgs.size()) {
7088+
typeVarReplacements.push_back(makeTypeVar());
7089+
} else {
7090+
typeVarReplacements.push_back(
7091+
genericArgs[i].transformRec([&](Type t) -> std::optional<Type> {
7092+
if (t->is<PlaceholderType>()) {
7093+
return makeTypeVar();
7094+
} else {
7095+
return std::nullopt;
7096+
}
7097+
}));
7098+
}
7099+
}
70317100
}
70327101

70337102
// Replace any references to the result builder's generic params
7034-
// in the result type with the corresponding type variables.
7103+
// in the result type with the corresponding replacements.
70357104
auto subMap = SubstitutionMap::get(genericSig, typeVarReplacements,
70367105
LookUpConformanceInModule());
70377106

@@ -7045,12 +7114,19 @@ struct ResultBuilderUnboundTypeOpener {
70457114

70467115
auto solution = cs.solveSingle();
70477116

7048-
// If a solution exists, bind the result builder's generic params to the
7049-
// solved types.
7117+
// If a solution exists, build the final replacement list by substituting
7118+
// all type variables with their solved bindings.
70507119
if (solution) {
70517120
llvm::SmallVector<Type, 8> solvedReplacements;
7052-
for (auto typeVar : typeVars) {
7053-
solvedReplacements.push_back(solution->typeBindings[typeVar]);
7121+
for (auto replacement : typeVarReplacements) {
7122+
solvedReplacements.push_back(
7123+
replacement.transformRec([&](Type t) -> std::optional<Type> {
7124+
if (auto *typeVar = t->getAs<TypeVariableType>()) {
7125+
return solution->typeBindings[typeVar];
7126+
} else {
7127+
return std::nullopt;
7128+
}
7129+
}));
70547130
}
70557131

70567132
return BoundGenericType::get(unboundTyDecl, unboundTy->getParent(),
@@ -7148,6 +7224,7 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr,
71487224
const TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl);
71497225

71507226
OpenUnboundGenericTypeFn unboundTyOpener = nullptr;
7227+
HandlePlaceholderTypeReprFn placeholderHandler = nullptr;
71517228

71527229
// Property wrappers and result builders allow their type to be an unbound
71537230
// generic.
@@ -7158,11 +7235,19 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr,
71587235
void *mem = ctx.Allocate(sizeof(ResultBuilderUnboundTypeOpener),
71597236
alignof(ResultBuilderUnboundTypeOpener));
71607237
unboundTyOpener = *new (mem) ResultBuilderUnboundTypeOpener{dc, attr};
7238+
placeholderHandler = PlaceholderType::get;
71617239
}
71627240

71637241
auto type = TypeResolution::resolveContextualType(
71647242
attr->getTypeRepr(), dc, options, unboundTyOpener,
7165-
/*placeholderHandler*/ nullptr, /*packElementOpener*/ nullptr);
7243+
/*placeholderHandler*/ placeholderHandler,
7244+
/*packElementOpener*/ nullptr);
7245+
7246+
// Open any pl
7247+
if (typeKind == CustomAttrTypeKind::ResultBuilder && type->hasPlaceholder()) {
7248+
ResultBuilderUnboundTypeOpener opener{dc, attr};
7249+
type = opener.openPlaceholders(type);
7250+
}
71667251

71677252
// We always require the type to resolve to a nominal type. If the type was
71687253
// not a nominal type, we should have already diagnosed an error via

test/Constraints/result_builder_diags.swift

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,17 @@ enum SimpleArrayBuilder<ElementKind> {
10871087
}
10881088
}
10891089

1090+
@resultBuilder
1091+
enum SimpleDictionaryBuilder<Key: Hashable, Value> {
1092+
static func buildBlock(_ elements: (Key, Value)...) -> [Key: Value] {
1093+
var dict = [Key: Value]()
1094+
for element in elements {
1095+
dict[element.0] = element.1
1096+
}
1097+
return dict
1098+
}
1099+
}
1100+
10901101
@resultBuilder
10911102
enum CollectionBuilder<Element> {
10921103
static func buildBlock(_ component: Element...) -> [Element] {
@@ -1124,6 +1135,67 @@ func testInferResultBuilderGenerics() {
11241135
"foo"
11251136
}
11261137

1138+
@SimpleArrayBuilder<String>
1139+
var elements: [String] {
1140+
"foo"
1141+
"bar"
1142+
}
1143+
1144+
@SimpleArrayBuilder<(_, _)>
1145+
var elements2: [(String, Int)] {
1146+
("foo", 42)
1147+
}
1148+
1149+
@SimpleArrayBuilder<(String, _)>
1150+
var elements3: [(String, Int)] {
1151+
("foo", 42)
1152+
}
1153+
1154+
@SimpleArrayBuilder<(_, Int)>
1155+
var elements4: [(String, Int)] {
1156+
("foo", 42)
1157+
}
1158+
1159+
@SimpleArrayBuilder<(Int, _)> // expected-error {{unable to infer generic arguments for result builder @SimpleArrayBuilder<(Int, _)>}} expeced-error {{cannot convert return expression of type '(String, Int)' to return type '[(String, Int)]'}}
1160+
var elements5: [(String, Int)] {
1161+
("foo", 42) // expected-error {{cannot convert return expression of type '(String, Int)' to return type '[(String, Int)]'}}
1162+
}
1163+
1164+
@SimpleArrayBuilder<(_, String)>
1165+
var elements6: [(String, Int)] { // expected-error {{cannot convert return expression of type '[(String, String)]' to return type '[(String, Int)]'}} expected-note {{arguments to generic parameter 'Element' ('(String, String)' and '(String, Int)') are expected to be equal}}
1166+
("foo", 42) // expected-error {{cannot convert value of type '(String, Int)' to expected argument type '(String, String)'}}
1167+
}
1168+
1169+
@SimpleArrayBuilder<(Int, String)>
1170+
var elements7: [(String, Int)] { // expected-error {{cannot convert return expression of type '[(Int, String)]' to return type '[(String, Int)]'}} expected-note {{arguments to generic parameter 'Element' ('(Int, String)' and '(String, Int)') are expected to be equal}}
1171+
("foo", 42) // expected-error {{cannot convert value of type '(String, Int)' to expected argument type '(Int, String)'}}
1172+
}
1173+
1174+
@SimpleDictionaryBuilder
1175+
var dictionary: [String: Int] {
1176+
("foo", 42)
1177+
}
1178+
1179+
@SimpleDictionaryBuilder<_, _>
1180+
var dictionary2: [String: Int] {
1181+
("foo", 42)
1182+
}
1183+
1184+
@SimpleDictionaryBuilder<String, _>
1185+
var dictionary3: [String: Int] {
1186+
("foo", 42)
1187+
}
1188+
1189+
@SimpleDictionaryBuilder<_, Int>
1190+
var dictionary4: [String: Int] {
1191+
("foo", 42)
1192+
}
1193+
1194+
@SimpleDictionaryBuilder<Int, _>
1195+
var dictionary5: [String: Int] { // expected-error {{cannot convert return expression of type '[Int : Int]' to return type '[String : Int]'}} expected-note {{arguments to generic parameter 'Key' ('Int' and 'String') are expected to be equal}}
1196+
("foo", 42) // expected-error {{cannot convert value of type '(String, Int)' to expected argument type '(Int, Int)'}}
1197+
}
1198+
11271199
struct MyValue {
11281200
@SimpleArrayBuilder var array1: [String]
11291201
@SimpleArrayBuilder var array2: () -> [String]
@@ -1316,4 +1388,41 @@ func testNonGenericResultBuildersInGenericTypeExtensions() {
13161388
("foo", "bar") // expected-warning {{expression of type '(String, String)' is unused}}
13171389
("baaz", "quux") // expected-warning {{expression of type '(String, String)' is unused}}
13181390
}
1391+
1392+
@Array<_>.Builder
1393+
var elements2: [String] {
1394+
"foo"
1395+
"bar"
1396+
}
1397+
1398+
@Array<String>.Builder
1399+
var elements3: [String] {
1400+
"foo"
1401+
"bar"
1402+
}
1403+
1404+
@Array<(_, _)>.Builder
1405+
var elements4: [(String, Int)] {
1406+
("foo", 42)
1407+
}
1408+
1409+
@Array<(String, _)>.Builder
1410+
var elements5: [(String, Int)] {
1411+
("foo", 42)
1412+
}
1413+
1414+
@Array<(_, Int)>.Builder
1415+
var elements6: [(String, Int)] {
1416+
("foo", 42)
1417+
}
1418+
1419+
@Array<(Int, _)>.Builder // expected-error {{unable to infer generic arguments for result builder @Array<(Int, _)>.Builder}}
1420+
var elements7: [(String, Int)] {
1421+
("foo", 42) // expected-error {{cannot convert return expression of type '(String, Int)' to return type '[(String, Int)]'}}
1422+
}
1423+
1424+
@Array<(Int, String)>.Builder
1425+
var elements8: [(String, Int)] { // expected-error {{cannot convert return expression of type '[(Int, String)]' to return type '[(String, Int)]'}} expected-note {{arguments to generic parameter 'Element' ('(Int, String)' and '(String, Int)') are expected to be equal}}
1426+
("foo", 42) // expected-error {{cannot convert value of type '(String, Int)' to expected argument type '(Int, String)'}}
1427+
}
13191428
}

0 commit comments

Comments
 (0)