Skip to content

Commit ace5c20

Browse files
authored
Merge pull request #78248 from Azoy/value-generic-static-member
[NameLookup] Allow value generics to show up as static members
2 parents 2dd6348 + 79a51df commit ace5c20

16 files changed

+256
-38
lines changed

include/swift/AST/Expr.h

+13-13
Original file line numberDiff line numberDiff line change
@@ -1449,23 +1449,25 @@ class TypeExpr : public Expr {
14491449
};
14501450

14511451
class TypeValueExpr : public Expr {
1452-
GenericTypeParamDecl *paramDecl;
1453-
DeclNameLoc loc;
1452+
TypeRepr *repr;
14541453
Type paramType;
14551454

1456-
/// Create a \c TypeValueExpr from a given generic value param decl.
1457-
TypeValueExpr(DeclNameLoc loc, GenericTypeParamDecl *paramDecl) :
1458-
Expr(ExprKind::TypeValue, /*implicit*/ false), paramDecl(paramDecl),
1459-
loc(loc), paramType(nullptr) {}
1455+
/// Create a \c TypeValueExpr from a given type representation.
1456+
TypeValueExpr(TypeRepr *repr) :
1457+
Expr(ExprKind::TypeValue, /*implicit*/ false), repr(repr),
1458+
paramType(nullptr) {}
14601459

14611460
public:
1462-
/// Create a \c TypeValueExpr for a given \c GenericTypeParamDecl.
1461+
/// Create a \c TypeValueExpr for a given \c TypeDecl.
14631462
///
14641463
/// The given location must be valid.
1465-
static TypeValueExpr *createForDecl(DeclNameLoc Loc, GenericTypeParamDecl *D);
1464+
static TypeValueExpr *createForDecl(DeclNameLoc loc, TypeDecl *d,
1465+
DeclContext *dc);
14661466

1467-
GenericTypeParamDecl *getParamDecl() const {
1468-
return paramDecl;
1467+
GenericTypeParamDecl *getParamDecl() const;
1468+
1469+
TypeRepr *getRepr() const {
1470+
return repr;
14691471
}
14701472

14711473
/// Retrieves the corresponding parameter type of the value referenced by this
@@ -1480,9 +1482,7 @@ class TypeValueExpr : public Expr {
14801482
this->paramType = paramType;
14811483
}
14821484

1483-
SourceRange getSourceRange() const {
1484-
return loc.getSourceRange();
1485-
}
1485+
SourceRange getSourceRange() const;
14861486

14871487
static bool classof(const Expr *E) {
14881488
return E->getKind() == ExprKind::TypeValue;

include/swift/Basic/Features.def

+1
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ LANGUAGE_FEATURE(RawIdentifiers, 451, "Raw identifiers")
256256
LANGUAGE_FEATURE(SendableCompletionHandlers, 463, "Objective-C completion handler parameters are imported as @Sendable")
257257
LANGUAGE_FEATURE(AsyncExecutionBehaviorAttributes, 0, "@concurrent and nonisolated(nonsending)")
258258
LANGUAGE_FEATURE(IsolatedConformances, 407, "Global-actor isolated conformances")
259+
LANGUAGE_FEATURE(ValueGenericsNameLookup, 452, "Value generics appearing as static members for namelookup")
259260

260261
// Swift 6
261262
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)

lib/AST/Expr.cpp

+15-4
Original file line numberDiff line numberDiff line change
@@ -2441,11 +2441,22 @@ bool Expr::isSelfExprOf(const AbstractFunctionDecl *AFD, bool sameBase) const {
24412441
return false;
24422442
}
24432443

2444-
TypeValueExpr *TypeValueExpr::createForDecl(DeclNameLoc loc,
2445-
GenericTypeParamDecl *paramDecl) {
2446-
auto &ctx = paramDecl->getASTContext();
2444+
TypeValueExpr *TypeValueExpr::createForDecl(DeclNameLoc loc, TypeDecl *decl,
2445+
DeclContext *dc) {
2446+
auto &ctx = decl->getASTContext();
24472447
ASSERT(loc.isValid());
2448-
return new (ctx) TypeValueExpr(loc, paramDecl);
2448+
auto repr = UnqualifiedIdentTypeRepr::create(ctx, loc, decl->createNameRef());
2449+
repr->setValue(decl, dc);
2450+
return new (ctx) TypeValueExpr(repr);
2451+
}
2452+
2453+
GenericTypeParamDecl *TypeValueExpr::getParamDecl() const {
2454+
auto declRefRepr = cast<DeclRefTypeRepr>(getRepr());
2455+
return cast<GenericTypeParamDecl>(declRefRepr->getBoundDecl());
2456+
}
2457+
2458+
SourceRange TypeValueExpr::getSourceRange() const {
2459+
return getRepr()->getSourceRange();
24492460
}
24502461

24512462
ExistentialArchetypeType *OpenExistentialExpr::getOpenedArchetype() const {

lib/AST/FeatureSet.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "FeatureSet.h"
1414

15+
#include "swift/AST/ASTWalker.h"
1516
#include "swift/AST/Decl.h"
1617
#include "swift/AST/ExistentialLayout.h"
1718
#include "swift/AST/GenericParamList.h"
@@ -470,6 +471,54 @@ static bool usesFeatureValueGenerics(Decl *decl) {
470471
return false;
471472
}
472473

474+
class UsesTypeValueExpr : public ASTWalker {
475+
public:
476+
bool used = false;
477+
478+
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
479+
if (isa<TypeValueExpr>(expr)) {
480+
used = true;
481+
return Action::Stop();
482+
}
483+
484+
return Action::Continue(expr);
485+
}
486+
};
487+
488+
static bool usesFeatureValueGenericsNameLookup(Decl *decl) {
489+
// Be conservative and mark any function that has a TypeValueExpr in its body
490+
// as having used this feature. It's a little difficult to fine grain this
491+
// check because the following:
492+
//
493+
// func a() -> Int {
494+
// A<123>.n
495+
// }
496+
//
497+
// Would appear to have the same expression as something like:
498+
//
499+
// extension A where n == 123 {
500+
// func b() -> Int {
501+
// n
502+
// }
503+
// }
504+
505+
auto fn = dyn_cast<AbstractFunctionDecl>(decl);
506+
507+
if (!fn)
508+
return false;
509+
510+
auto body = fn->getMacroExpandedBody();
511+
512+
if (!body)
513+
return false;
514+
515+
UsesTypeValueExpr utve;
516+
517+
body->walk(utve);
518+
519+
return utve.used;
520+
}
521+
473522
static bool usesFeatureCoroutineAccessors(Decl *decl) {
474523
auto accessorDeclUsesFeatureCoroutineAccessors = [](AccessorDecl *accessor) {
475524
return requiresFeatureCoroutineAccessors(accessor->getAccessorKind());

lib/AST/NameLookup.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -2739,6 +2739,18 @@ QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC,
27392739
}
27402740
}
27412741

2742+
// Qualified name lookup can find generic value parameters.
2743+
auto gpList = current->getGenericParams();
2744+
2745+
// .. But not in type contexts (yet)
2746+
if (!(options & NL_OnlyTypes) && gpList && !member.isSpecial()) {
2747+
auto gp = gpList->lookUpGenericParam(member.getBaseIdentifier());
2748+
2749+
if (gp && gp->isValue()) {
2750+
decls.push_back(gp);
2751+
}
2752+
}
2753+
27422754
// If we're not looking at a protocol and we're not supposed to
27432755
// visit the protocols that this type conforms to, skip the next
27442756
// step.

lib/Sema/CSApply.cpp

+36-1
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,29 @@ namespace {
16311631
// Build a member reference.
16321632
auto memberRef = resolveConcreteDeclRef(member, memberLocator);
16331633

1634+
// If our member reference is a value generic, then the resulting
1635+
// expression is the type value one to access the underlying parameter's
1636+
// value.
1637+
//
1638+
// This can occur in code that does something like: 'type(of: x).a' where
1639+
// 'a' is the static value generic member.
1640+
if (auto gp = dyn_cast<GenericTypeParamDecl>(member)) {
1641+
if (gp->isValue()) {
1642+
auto refType = adjustedOpenedType;
1643+
auto ref = TypeValueExpr::createForDecl(memberLoc, gp, dc);
1644+
cs.setType(ref, refType);
1645+
1646+
auto gpTy = gp->getDeclaredInterfaceType();
1647+
auto subs = baseTy->getContextSubstitutionMap();
1648+
ref->setParamType(gpTy.subst(subs));
1649+
1650+
auto result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc, ref,
1651+
refType);
1652+
cs.setType(result, refType);
1653+
return result;
1654+
}
1655+
}
1656+
16341657
// If we're referring to a member type, it's just a type
16351658
// reference.
16361659
if (auto *TD = dyn_cast<TypeDecl>(member)) {
@@ -3222,8 +3245,20 @@ namespace {
32223245

32233246
Expr *visitTypeValueExpr(TypeValueExpr *expr) {
32243247
auto toType = simplifyType(cs.getType(expr));
3225-
assert(toType->isEqual(expr->getParamDecl()->getValueType()));
3248+
ASSERT(toType->isEqual(expr->getParamDecl()->getValueType()));
32263249
cs.setType(expr, toType);
3250+
3251+
auto declRefRepr = cast<DeclRefTypeRepr>(expr->getRepr());
3252+
auto resolvedTy =
3253+
TypeResolution::resolveContextualType(declRefRepr, cs.DC,
3254+
TypeResolverContext::InExpression,
3255+
nullptr, nullptr, nullptr);
3256+
3257+
if (!resolvedTy || resolvedTy->hasError())
3258+
return nullptr;
3259+
3260+
expr->setParamType(resolvedTy);
3261+
32273262
return expr;
32283263
}
32293264

lib/Sema/CSGen.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -1723,9 +1723,6 @@ namespace {
17231723
}
17241724

17251725
Type visitTypeValueExpr(TypeValueExpr *E) {
1726-
auto ty = E->getParamDecl()->getDeclaredInterfaceType();
1727-
auto paramType = CS.DC->mapTypeIntoContext(ty);
1728-
E->setParamType(paramType);
17291726
return E->getParamDecl()->getValueType();
17301727
}
17311728

lib/Sema/PreCheckTarget.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -817,8 +817,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
817817
: D->getInterfaceType());
818818
} else {
819819
if (makeTypeValue) {
820-
return TypeValueExpr::createForDecl(UDRE->getNameLoc(),
821-
cast<GenericTypeParamDecl>(D));
820+
return TypeValueExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
822821
} else {
823822
return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
824823
}
@@ -1853,7 +1852,7 @@ TypeExpr *PreCheckTarget::simplifyUnresolvedSpecializeExpr(
18531852
UnresolvedSpecializeExpr *us) {
18541853
// If this is a reference type a specialized type, form a TypeExpr.
18551854
// The base should be a TypeExpr that we already resolved.
1856-
if (auto *te = dyn_cast<TypeExpr>(us->getSubExpr())) {
1855+
if (auto *te = dyn_cast_or_null<TypeExpr>(us->getSubExpr())) {
18571856
if (auto *declRefTR =
18581857
dyn_cast_or_null<DeclRefTypeRepr>(te->getTypeRepr())) {
18591858
return TypeExpr::createForSpecializedDecl(

lib/Sema/TypeCheckDeclPrimary.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,20 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
728728
auto found = nominal->lookupDirect(current->getBaseName(), SourceLoc(),
729729
flags);
730730
otherDefinitions.append(found.begin(), found.end());
731+
732+
// Look into the generics of the type. Value generic parameters can appear
733+
// as static members of the type.
734+
if (auto genericDC = static_cast<Decl *>(nominal)->getAsGenericContext()) {
735+
auto gpList = genericDC->getGenericParams();
736+
737+
if (gpList && !current->getBaseName().isSpecial()) {
738+
auto gp = gpList->lookUpGenericParam(current->getBaseIdentifier());
739+
740+
if (gp && gp->isValue()) {
741+
otherDefinitions.push_back(gp);
742+
}
743+
}
744+
}
731745
}
732746
} else if (currentDC->isLocalContext()) {
733747
if (!current->isImplicit()) {
@@ -1135,6 +1149,7 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
11351149
break;
11361150
}
11371151
}
1152+
11381153
return std::make_tuple<>();
11391154
}
11401155

lib/Sema/TypeCheckType.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -6335,6 +6335,18 @@ Type TypeChecker::substMemberTypeWithBase(TypeDecl *member,
63356335
resultType = TypeAliasType::get(aliasDecl, sugaredBaseTy, {}, resultType);
63366336
}
63376337

6338+
// However, if overload resolution finds a value generic decl from name
6339+
// lookup, replace the returned member type to be the underlying value type
6340+
// of the generic.
6341+
//
6342+
// This can occur in code that does something like: 'type(of: x).a' where
6343+
// 'a' is the static value generic member.
6344+
if (auto gp = dyn_cast<GenericTypeParamDecl>(member)) {
6345+
if (gp->isValue()) {
6346+
resultType = gp->getValueType();
6347+
}
6348+
}
6349+
63386350
return resultType;
63396351
}
63406352

lib/Sema/TypeOfReference.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,13 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
15571557
// Wrap it in a metatype.
15581558
memberTy = MetatypeType::get(memberTy);
15591559

1560+
// If this is a value generic, undo the wrapping. 'substMemberTypeWithBase'
1561+
// returns the underlying value type of the value generic (e.g. 'Int').
1562+
if (isa<GenericTypeParamDecl>(value) &&
1563+
cast<GenericTypeParamDecl>(value)->isValue()) {
1564+
memberTy = memberTy->castTo<MetatypeType>()->getInstanceType();
1565+
}
1566+
15601567
auto openedType = FunctionType::get({baseObjParam}, memberTy);
15611568
return { openedType, openedType, memberTy, memberTy, Type() };
15621569
}

stdlib/public/core/InlineArray.swift

-8
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,6 @@ extension InlineArray where Element: ~Copyable {
272272
@available(SwiftStdlib 6.2, *)
273273
public typealias Index = Int
274274

275-
// FIXME: Remove when SE-0452 "Integer Generic Parameters" is implemented.
276-
@available(SwiftStdlib 6.2, *)
277-
@_alwaysEmitIntoClient
278-
@_transparent
279-
public static var count: Int {
280-
count
281-
}
282-
283275
/// The number of elements in the array.
284276
///
285277
/// - Complexity: O(1)

test/ModuleInterface/value_generics.swift

+26
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public struct Slab<Element, let N: Int> {
1414
public var count: Int {
1515
N
1616
}
17+
18+
public init() {}
1719
}
1820

1921
// CHECK: public func usesGenericSlab<let N : Swift.Int>(_: ValueGeneric.Slab<Swift.Int, N>)
@@ -24,3 +26,27 @@ public func usesConcreteSlab(_: Slab<Int, 2>) {}
2426

2527
// CHECK: public func usesNegativeSlab(_: ValueGeneric.Slab<Swift.String, -10>)
2628
public func usesNegativeSlab(_: Slab<String, -10>) {}
29+
30+
// CHECK: $ValueGenericsNameLookup
31+
@inlinable
32+
public func test() -> Int {
33+
// CHECK: Slab<Int, 123>.N
34+
Slab<Int, 123>.N
35+
}
36+
37+
// CHECK: $ValueGenericsNameLookup
38+
@inlinable
39+
public func test2() -> Int {
40+
// CHECK: type(of: Slab<Int, 123>()).N
41+
type(of: Slab<Int, 123>()).N
42+
}
43+
44+
// CHECK: $ValueGenericsNameLookup
45+
@inlinable
46+
public func test3() {
47+
{
48+
print(123)
49+
print(123)
50+
print(Slab<Int, 123>.N)
51+
}()
52+
}

0 commit comments

Comments
 (0)