From a5e26fdb124880e068ed553770331737b554715c Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 18 Sep 2024 10:30:10 -0700 Subject: [PATCH 1/4] Restrict parsing integer types in certain contexts --- include/swift/Parse/Parser.h | 5 +++++ lib/Parse/ParseGeneric.cpp | 8 +++++-- lib/Parse/ParseType.cpp | 40 +++++++++++++++++++++++----------- test/Parse/integer_types.swift | 24 ++++++++++++++++++-- test/Sema/value_generics.swift | 13 ++++++----- 5 files changed, 68 insertions(+), 22 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 97289c64d8a35..a1728fa95e1d3 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1435,6 +1435,11 @@ class Parser { ParserResult parseTypeSimple( Diag<> MessageID, ParseTypeReason reason); + ParserResult parseTypeOrValue(); + ParserResult parseTypeOrValue(Diag<> MessageID, + ParseTypeReason reason = ParseTypeReason::Unspecified, + bool fromASTGen = false); + /// Parse layout constraint. LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID); diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index ffd84b5ea50df..818e3bab7f79e 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -317,7 +317,9 @@ ParserStatus Parser::parseGenericWhereClause( // Parse the leading type. It doesn't necessarily have to be just a type // identifier if we're dealing with a same-type constraint. - ParserResult FirstType = parseType(); + // + // Note: This can be a value type, e.g. '123 == N' or 'N == 123'. + ParserResult FirstType = parseTypeOrValue(); if (FirstType.hasCodeCompletion()) { Status.setHasCodeCompletionAndIsError(); @@ -377,7 +379,9 @@ ParserStatus Parser::parseGenericWhereClause( SourceLoc EqualLoc = consumeToken(); // Parse the second type. - ParserResult SecondType = parseType(); + // + // Note: This can be a value type, e.g. '123 == N' or 'N == 123'. + ParserResult SecondType = parseTypeOrValue(); Status |= SecondType; if (SecondType.isNull()) SecondType = makeParserResult(ErrorTypeRepr::create(Context, PreviousLoc)); diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index ae9bfd6043aa6..d32a3a0862bf9 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -175,12 +175,6 @@ ParserResult Parser::parseTypeSimple( tildeLoc = consumeToken(); } - // Eat any '-' preceding integer literals. - SourceLoc minusLoc; - if (Tok.isMinus() && peekToken().is(tok::integer_literal)) { - minusLoc = consumeToken(); - } - switch (Tok.getKind()) { case tok::kw_Self: case tok::identifier: @@ -237,12 +231,6 @@ ParserResult Parser::parseTypeSimple( } return makeParserCodeCompletionResult( ErrorTypeRepr::create(Context, consumeToken(tok::code_complete))); - case tok::integer_literal: { - auto text = copyAndStripUnderscores(Tok.getText()); - auto loc = consumeToken(tok::integer_literal); - ty = makeParserResult(new (Context) IntegerTypeRepr(text, loc, minusLoc)); - break; - } case tok::l_square: { ty = parseTypeCollection(); break; @@ -739,7 +727,8 @@ ParserStatus Parser::parseGenericArguments(SmallVectorImpl &Args, // variadic generic types. if (!startsWithGreater(Tok)) { while (true) { - ParserResult Ty = parseType(diag::expected_type); + // Note: This can be a value type, e.g. 'Vector<3, Int>'. + ParserResult Ty = parseTypeOrValue(diag::expected_type); if (Ty.isNull() || Ty.hasCodeCompletion()) { // Skip until we hit the '>'. RAngleLoc = skipUntilGreaterInTypeList(); @@ -1481,6 +1470,31 @@ Parser::parseTypeImplicitlyUnwrappedOptional(ParserResult base) { return makeParserResult(ParserStatus(base), TyR); } +ParserResult Parser::parseTypeOrValue() { + return parseTypeOrValue(diag::expected_type); +} + +ParserResult Parser::parseTypeOrValue(Diag<> MessageID, + ParseTypeReason reason, + bool fromASTGen) { + // Eat any '-' preceding integer literals. + SourceLoc minusLoc; + if (Tok.isMinus() && peekToken().is(tok::integer_literal)) { + minusLoc = consumeToken(); + } + + // Attempt to parse values first. Right now the only value that can be parsed + // as a type are integers. + if (Tok.is(tok::integer_literal)) { + auto text = copyAndStripUnderscores(Tok.getText()); + auto loc = consumeToken(tok::integer_literal); + return makeParserResult(new (Context) IntegerTypeRepr(text, loc, minusLoc)); + } + + // Otherwise, attempt to parse a regular type. + return parseType(MessageID, reason, fromASTGen); +} + //===----------------------------------------------------------------------===// // Speculative type list parsing //===----------------------------------------------------------------------===// diff --git a/test/Parse/integer_types.swift b/test/Parse/integer_types.swift index 3ac09e87669ff..8d4ec0f4be092 100644 --- a/test/Parse/integer_types.swift +++ b/test/Parse/integer_types.swift @@ -1,8 +1,12 @@ // RUN: %target-typecheck-verify-swift -let a: 123 // expected-error {{integer unexpectedly used in a type position}} +let a: 123 // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-warning@-2 {{integer literal is unused}} -let b: -123 // expected-error {{integer unexpectedly used in a type position}} +let b: -123 // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-warning@-2 {{integer literal is unused}} let c: -Int // expected-error {{expected type}} // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} @@ -30,3 +34,19 @@ let f = Generic<-Int>.self // expected-error {{generic parameter 'T' could not b // expected-error@-1 {{missing whitespace between '<' and '-' operators}} // expected-error@-2 {{'>' is not a postfix unary operator}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} + +let g: 123.Type // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-error@-2 {{value of type 'Int' has no member 'Type'}} + +let h: 123.Protocol // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-error@-2 {{value of type 'Int' has no member 'Protocol'}} + +let i: 123? // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-error@-2 {{cannot use optional chaining on non-optional value of type 'Int'}} + +let j: 123! // expected-error {{consecutive statements on a line must be separated by ';'}} + // expected-error@-1 {{expected type}} + // expected-error@-2 {{cannot force unwrap value of non-optional type 'Int'}} diff --git a/test/Sema/value_generics.swift b/test/Sema/value_generics.swift index 231b86d81f6e4..b2ad741a658fa 100644 --- a/test/Sema/value_generics.swift +++ b/test/Sema/value_generics.swift @@ -49,18 +49,21 @@ struct Generic {} struct GenericWithIntParam {} extension Generic where T == 123 {} // expected-error {{cannot constrain type parameter 'T' to be integer '123'}} -extension Generic where T == 123.Type {} // expected-error {{integer unexpectedly used in a type position}} +extension Generic where T == 123.Type {} // expected-error {{cannot constrain type parameter 'T' to be integer '123'}} + // expected-error@-1 {{expected '{' in extension}} +extension Generic where T == 123? {} // expected-error {{cannot constrain type parameter 'T' to be integer '123'}} + // expected-error@-1 {{expected '{' in extension}} func f(_: Generic<123>) {} // expected-error {{integer unexpectedly used in a type position}} func g(_: Generic) {} // expected-error {{cannot use value type 'N' for generic argument 'T'}} -func h(_: (Int, 123)) {} // expected-error {{integer unexpectedly used in a type position}} -func i(_: () -> 123) {} // expected-error {{integer unexpectedly used in a type position}} +func h(_: (Int, 123)) {} // expected-error {{expected type}} +func i(_: () -> 123) {} // expected-error {{expected type}} func j(_: (A<123>) -> ()) {} // OK -func k(_: some 123) {} // expected-error {{integer unexpectedly used in a type position}} +func k(_: some 123) {} // expected-error {{expected parameter type following ':'}} func l(_: GenericWithIntParam<123, Int>) {} // expected-error {{cannot pass type 'Int' as a value for generic value 'N'}} func m(_: GenericWithIntParam) {} // OK -typealias One = 1 // expected-error {{integer unexpectedly used in a type position}} +typealias One = 1 // expected-error {{expected type in type alias declaration}} struct B {} // expected-error {{'UInt8' is not a supported value type for 'N'}} From cd0a9271c7d4a563e572c8b38f1afd54658f07b0 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 18 Sep 2024 13:14:19 -0700 Subject: [PATCH 2/4] Allow raw layout to parse integer types --- lib/Parse/ParseDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 478e66b10d8ae..32ca7d46c92d3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4116,7 +4116,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, return makeParserSuccess(); } - auto countType = parseType(diag::expected_type); + auto countType = parseTypeOrValue(diag::expected_type); if (countType.isNull()) { return makeParserSuccess(); } From 06999e775d2ff59533d0d72cee9bd02f391d8ba9 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 19 Sep 2024 11:16:22 -0700 Subject: [PATCH 3/4] Explicitly call parseTypeOrValue for type value instructions --- lib/SIL/Parser/ParseSIL.cpp | 38 ++++++++++++++++++++++++++++++++++++- lib/SIL/Parser/SILParser.h | 5 +++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 2947496fa0188..ffa45390661cf 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1060,6 +1060,42 @@ bool SILParser::parseASTType(CanType &result, return false; } +bool SILParser::parseASTTypeOrValue(CanType &result, + GenericSignature genericSig, + GenericParamList *genericParams, + bool forceContextualType) { + auto parsedType = P.parseTypeOrValue(); + if (parsedType.isNull()) return true; + + // If we weren't given a specific generic context to resolve the type + // within, use the contextual generic parameters and always produce + // a contextual type. Otherwise, produce a contextual type only if + // we were asked for one. + bool wantContextualType = forceContextualType; + if (!genericSig) { + genericSig = ContextGenericSig; + wantContextualType = true; + } + if (genericParams == nullptr) + genericParams = ContextGenericParams; + + bindSILGenericParams(parsedType.get()); + + auto resolvedType = performTypeResolution( + parsedType.get(), /*isSILType=*/false, genericSig, genericParams); + if (wantContextualType && genericSig) { + resolvedType = genericSig.getGenericEnvironment() + ->mapTypeIntoContext(resolvedType); + } + + if (resolvedType->hasError()) + return true; + + result = resolvedType->getCanonicalType(); + + return false; +} + void SILParser::bindSILGenericParams(TypeRepr *TyR) { // Resolve the generic environments for parsed generic function and box types. class HandleSILGenericParamsWalker : public ASTWalker { @@ -3097,7 +3133,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, CanType paramType; if (parseSILType(Ty) || parseVerbatim("for") || - parseASTType(paramType)) + parseASTTypeOrValue(paramType)) return true; ResultVal = B.createTypeValue(InstLoc, Ty, paramType); diff --git a/lib/SIL/Parser/SILParser.h b/lib/SIL/Parser/SILParser.h index 9a6c90f089ce6..df1a75543b881 100644 --- a/lib/SIL/Parser/SILParser.h +++ b/lib/SIL/Parser/SILParser.h @@ -235,6 +235,11 @@ class SILParser { return false; } + bool parseASTTypeOrValue(CanType &result, + GenericSignature genericSig = GenericSignature(), + GenericParamList *genericParams = nullptr, + bool forceContextualType = false); + std::optional parseOptionalAttribute(ArrayRef expected) { // We parse here @ . From 22b945d904213af21e937c47152de3f1f70659b6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 24 Sep 2024 11:02:52 -0700 Subject: [PATCH 4/4] Update tests to old behavior --- test/attr/attr_implements_bad_parse.swift | 2 +- test/expr/postfix/init/unqualified.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/attr/attr_implements_bad_parse.swift b/test/attr/attr_implements_bad_parse.swift index 17f0deb0bfe34..82f7f990d1709 100644 --- a/test/attr/attr_implements_bad_parse.swift +++ b/test/attr/attr_implements_bad_parse.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-frontend -parse -verify %s struct S0 { - @_implements(1, Foo) // OK. We can parse integers as types now, so this is fine. We will diagnose its incorrect usage during type checking. + @_implements(1, Foo) // expected-error {{expected type}} func f() { } } diff --git a/test/expr/postfix/init/unqualified.swift b/test/expr/postfix/init/unqualified.swift index 645bef69ec32b..bb8372378d2c2 100644 --- a/test/expr/postfix/init/unqualified.swift +++ b/test/expr/postfix/init/unqualified.swift @@ -62,7 +62,7 @@ class Theodosia: Aaron { // FIXME: We could optimistically parse this as an expression instead // expected-error@+2 {{initializers may only be declared within a type}} - // expected-error@+1 {{integer unexpectedly used in a type position}} + // expected-error@+1 {{expected parameter type following ':'}} init(z: 0) } @@ -98,7 +98,7 @@ struct AaronStruct { // FIXME: We could optimistically parse this as an expression instead // expected-error@+2 {{initializers may only be declared within a type}} - // expected-error@+1 {{integer unexpectedly used in a type position}} + // expected-error@+1 {{expected parameter type following ':'}} init(y: 1) }