Skip to content

[Parse] Fix type parsing when preceded by '-' #76404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8002,12 +8002,6 @@ NOTE(unsafe_decl_here,none,
// MARK: Value Generics
//===----------------------------------------------------------------------===//

ERROR(invalid_value_value_generic,none,
"%0 requires that %1 must be a valid value for %2",
(Type, Type, Type))
NOTE(invalid_value_value_generic_requirement,none,
"requirement specified as %0 == %1%2",
(Type, Type, StringRef))
ERROR(cannot_pass_type_for_value_generic,none,
"cannot pass type %0 as a value for generic value %1", (Type, Type))
ERROR(value_type_used_in_type_parameter,none,
Expand All @@ -8029,6 +8023,8 @@ ERROR(value_generics_missing_feature,none,
ERROR(availability_value_generic_type_only_version_newer, none,
"values in generic types are only available in %0 %1 or newer",
(StringRef, llvm::VersionTuple))
ERROR(invalid_value_for_type_same_type,none,
"cannot constrain type parameter %0 to be integer %1", (Type, Type))

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
12 changes: 12 additions & 0 deletions lib/AST/RequirementMachine/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ bool swift::rewriting::diagnoseRequirementErrors(
diagnosedError = true;
break;
}

case RequirementError::Kind::InvalidValueForTypeSameType: {
auto req = error.getRequirement();

if (req.hasError())
break;

ctx.Diags.diagnose(loc, diag::invalid_value_for_type_same_type,
req.getFirstType(), req.getSecondType());
diagnosedError = true;
break;
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions lib/AST/RequirementMachine/Diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct RequirementError {
/// A value generic type was used to same-type to an unrelated type,
/// e.g. 'where N == Int' where N == 'let N: Int'.
InvalidValueGenericSameType,
/// A value type, either an integer '123' or a value generic parameter 'N',
/// was used to same type a regular type parameter, e.g. 'T == 123'.
InvalidValueForTypeSameType,
} kind;

private:
Expand Down Expand Up @@ -177,6 +180,13 @@ struct RequirementError {
Requirement requirement(RequirementKind::Conformance, subjectType, constraint);
return {Kind::InvalidValueGenericSameType, requirement, loc};
}

static RequirementError forInvalidValueForTypeSameType(Type subjectType,
Type constraint,
SourceLoc loc) {
Requirement requirement(RequirementKind::Conformance, subjectType, constraint);
return {Kind::InvalidValueForTypeSameType, requirement, loc};
}
};

/// Policy for the fixit that transforms 'T : S' where 'S' is not a protocol
Expand Down
12 changes: 12 additions & 0 deletions lib/AST/RequirementMachine/RequirementLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,18 @@ static void desugarSameTypeRequirement(
}
}

if (!firstType->isValueParameter() && secondType->is<IntegerType>()) {
errors.push_back(RequirementError::forInvalidValueForTypeSameType(
sugaredFirstType, secondType, loc));
return true;
}

if (!secondType->isValueParameter() && firstType->is<IntegerType>()) {
errors.push_back(RequirementError::forInvalidValueForTypeSameType(
secondType, sugaredFirstType, loc));
return true;
}

if (firstType->isTypeParameter() && secondType->isTypeParameter()) {
result.emplace_back(kind, sugaredFirstType, secondType);
return true;
Expand Down
25 changes: 19 additions & 6 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ ParserResult<TypeRepr> Parser::parseTypeSimple(
tildeLoc = consumeToken();
}

// Eat any '-' preceding the type.
// Eat any '-' preceding integer literals.
SourceLoc minusLoc;
if (Tok.isMinus()) {
if (Tok.isMinus() && peekToken().is(tok::integer_literal)) {
minusLoc = consumeToken();
}

Expand Down Expand Up @@ -1588,13 +1588,26 @@ bool Parser::canParseType() {
return false;
break;
case tok::oper_prefix:
if (Tok.getText() != "~" && Tok.getText() != "-") {
if (!Tok.isTilde() && !Tok.isMinus()) {
return false;
}

consumeToken();
if (!canParseTypeIdentifier())
return false;
// '~' can only appear before type identifiers like '~Copyable'.
if (Tok.isTilde()) {
consumeToken();

if (!canParseTypeIdentifier())
return false;
}

// '-' can only appear before integers being used as types like '-123'.
if (Tok.isMinus()) {
consumeToken();

if (!Tok.is(tok::integer_literal))
return false;
}

break;
case tok::kw_protocol:
return canParseOldStyleProtocolComposition();
Expand Down
32 changes: 32 additions & 0 deletions test/Parse/integer_types.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %target-typecheck-verify-swift

let a: 123 // expected-error {{integer unexpectedly used in a type position}}

let b: -123 // expected-error {{integer unexpectedly used in a type position}}

let c: -Int // expected-error {{expected type}}
// expected-error@-1 {{consecutive statements on a line must be separated by ';'}}
// expected-error@-2 {{unary operator '-' cannot be applied to an operand of type 'Int.Type'}}

struct Generic<T> {} // expected-note {{'T' declared as parameter to type 'Generic'}}
// expected-note@-1 {{'T' declared as parameter to type 'Generic'}}

extension Generic where T == 123 {} // expected-error {{cannot constrain type parameter 'T' to be integer '123'}}

extension Generic where T == -123 {} // expected-error {{cannot constrain type parameter 'T' to be integer '-123'}}

extension Generic where T == -Int {} // expected-error {{expected type}}
// expected-error@-1 {{expected '{' in extension}}

let d = Generic<123>.self // expected-error {{integer unexpectedly used in a type position}}

// FIXME: This should at least be parsable...?
let e = Generic<-123>.self // expected-error {{generic parameter 'T' could not be inferred}}
// 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 f = Generic<-Int>.self // expected-error {{generic parameter 'T' could not be inferred}}
// 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}}
3 changes: 3 additions & 0 deletions test/Sema/value_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func e(with a: A<Int>) {} // expected-error {{cannot pass type 'Int' as a value
struct Generic<T: ~Copyable & ~Escapable> {}
struct GenericWithIntParam<T: ~Copyable & ~Escapable, let N: Int> {}

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

func f(_: Generic<123>) {} // expected-error {{integer unexpectedly used in a type position}}
func g<let N: Int>(_: Generic<N>) {} // expected-error {{cannot use value type 'N' for generic argument 'T'}}
func h(_: (Int, 123)) {} // expected-error {{integer unexpectedly used in a type position}}
Expand Down