Skip to content

Commit 64b3d7f

Browse files
committed
Merge pull request #78248 from Azoy/value-generic-static-member
[NameLookup] Allow value generics to show up as static members
1 parent b7d9f0f commit 64b3d7f

File tree

16 files changed

+256
-38
lines changed

16 files changed

+256
-38
lines changed

include/swift/AST/Expr.h

Lines changed: 13 additions & 13 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ LANGUAGE_FEATURE(ValueGenerics, 452, "Value generics feature (integer generics)"
255255
LANGUAGE_FEATURE(RawIdentifiers, 451, "Raw identifiers")
256256
LANGUAGE_FEATURE(SendableCompletionHandlers, 463, "Objective-C completion handler parameters are imported as @Sendable")
257257
LANGUAGE_FEATURE(IsolatedConformances, 470, "Global-actor isolated conformances")
258+
LANGUAGE_FEATURE(ValueGenericsNameLookup, 452, "Value generics appearing as static members for namelookup")
258259

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

lib/AST/Expr.cpp

Lines changed: 15 additions & 4 deletions
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

Lines changed: 49 additions & 0 deletions
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"
@@ -471,6 +472,54 @@ static bool usesFeatureValueGenerics(Decl *decl) {
471472
return false;
472473
}
473474

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

lib/AST/NameLookup.cpp

Lines changed: 12 additions & 0 deletions
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

Lines changed: 36 additions & 1 deletion
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)) {
@@ -3223,8 +3246,20 @@ namespace {
32233246

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

lib/Sema/CSGen.cpp

Lines changed: 0 additions & 3 deletions
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

Lines changed: 2 additions & 3 deletions
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

Lines changed: 15 additions & 0 deletions
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()) {
@@ -1136,6 +1150,7 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
11361150
break;
11371151
}
11381152
}
1153+
11391154
return std::make_tuple<>();
11401155
}
11411156

lib/Sema/TypeCheckType.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6273,6 +6273,18 @@ Type TypeChecker::substMemberTypeWithBase(TypeDecl *member,
62736273
resultType = TypeAliasType::get(aliasDecl, sugaredBaseTy, {}, resultType);
62746274
}
62756275

6276+
// However, if overload resolution finds a value generic decl from name
6277+
// lookup, replace the returned member type to be the underlying value type
6278+
// of the generic.
6279+
//
6280+
// This can occur in code that does something like: 'type(of: x).a' where
6281+
// 'a' is the static value generic member.
6282+
if (auto gp = dyn_cast<GenericTypeParamDecl>(member)) {
6283+
if (gp->isValue()) {
6284+
resultType = gp->getValueType();
6285+
}
6286+
}
6287+
62766288
return resultType;
62776289
}
62786290

lib/Sema/TypeOfReference.cpp

Lines changed: 7 additions & 0 deletions
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

Lines changed: 0 additions & 8 deletions
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

Lines changed: 26 additions & 0 deletions
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)