Skip to content
Closed
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
5 changes: 2 additions & 3 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9151,9 +9151,8 @@ class EnumElementDecl : public DeclContext, public ValueDecl {
/// exists, as it was written in the source.
Expr *getOriginalRawValueExpr() const;

/// Retrieves a fully-typechecked and (if LiteralExpressions experimental
/// feature is enabled) constant-folded raw value expression associated with
/// this enum element, if it exists.
/// Retrieves a fully-typechecked and constant-folded raw value expression
/// associated with this enum element, if it exists.
LiteralExpr *getRawValueExpr() const;

/// Reset the raw value expression.
Expand Down
2 changes: 0 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -9033,8 +9033,6 @@ ERROR(availability_value_generic_type_only_version_newer, none,
ERROR(invalid_value_for_type_same_type,none,
"cannot constrain type parameter %0 to be integer %1", (Type, Type))

ERROR(nonliteral_integer_generic_value,none,
"generic value must be an integer literal", ())
ERROR(nonliteral_integer_expr_generic_value,none,
"generic value must be an integer literal expression", ())
ERROR(integer_generic_expr_closure_not_supported,none,
Expand Down
12 changes: 9 additions & 3 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -5122,10 +5122,15 @@ evaluate(Evaluator &evaluator, SourceFile *SF) const;
bool isCached() const { return true; }
};

/// A request to constant-fold an expression node
/// A request to constant-fold an expression node.
///
/// \c emitDiagnostics distinguishes verification (emit diagnostics when the
/// expression cannot be folded to a literal) from code generation (fold
/// silently; a caller that also verifies owns the diagnostics). It is part of
/// the cache key so the two modes don't collide.
class ConstantFoldExpression
: public SimpleRequest<ConstantFoldExpression,
Expr *(const Expr *, ASTContext *),
Expr *(const Expr *, ASTContext *, bool),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -5134,7 +5139,8 @@ using SimpleRequest::SimpleRequest;
friend SimpleRequest;

Expr *
evaluate(Evaluator &evaluator, const Expr *expr, ASTContext *ctx) const;
evaluate(Evaluator &evaluator, const Expr *expr, ASTContext *ctx,
bool emitDiagnostics) const;

public:
bool isCached() const { return true; }
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -698,5 +698,5 @@ SWIFT_REQUEST(TypeChecker, DesugarForEachStmtRequest,
SeparatelyCached, NoLocationInfo)

SWIFT_REQUEST(TypeChecker, ConstantFoldExpression,
Expr *(const Expr *, ASTContext *),
Expr *(const Expr *, ASTContext *, bool),
Cached, NoLocationInfo)
2 changes: 1 addition & 1 deletion include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ EXPERIMENTAL_FEATURE(CompileTimeValues, true)
EXPERIMENTAL_FEATURE(CompileTimeValuesPreview, false)

/// Allow use of literal expressions as literals
EXPERIMENTAL_FEATURE(LiteralExpressions, true)
LANGUAGE_FEATURE(LiteralExpressions, 531, "literal expressions")

/// Allow function body macros applied to closures.
EXPERIMENTAL_FEATURE(ClosureBodyMacro, true)
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,12 @@ class Parser {
bool canParseGenericArguments();
bool canParseGenericValueLiteral();

/// Assuming the parser is positioned at the opening '(' of a generic argument
/// that would otherwise be parsed as a value expression, check if the
/// parenthesized group contains an opaque 'some' type that requires it to be
/// parsed as a type instead.
bool parenGenericArgumentContainsTypeOnlySyntax();

bool canParseTypedPattern();

/// Returns true if a qualified declaration name base type can be parsed.
Expand Down
20 changes: 7 additions & 13 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2765,19 +2765,13 @@ namespace {
// triggering EnumRawValuesRequest which requres type-checking.
if (isTypeChecked()) {
if (auto *rawValueExpr = EED->getRawValueExpr()) {
if (EED->getASTContext().LangOpts.hasFeature(
Feature::LiteralExpressions)) {
auto origRawValueExpr = EED->getOriginalRawValueExpr();
if (isa<LiteralExpr>(origRawValueExpr))
printRec(origRawValueExpr, Label::always("raw_value_expr"));
else {
printRec(EED->getOriginalRawValueExpr(),
Label::always("original_raw_value_expr"));
printRec(EED->getRawValueExpr(),
Label::always("folded_raw_value_expr"));
}
} else {
printRec(rawValueExpr, Label::always("raw_value_expr"));
auto origRawValueExpr = EED->getOriginalRawValueExpr();
if (isa<LiteralExpr>(origRawValueExpr))
printRec(origRawValueExpr, Label::always("raw_value_expr"));
else {
printRec(origRawValueExpr,
Label::always("original_raw_value_expr"));
printRec(rawValueExpr, Label::always("folded_raw_value_expr"));
}
}
} else if (auto *rawValueExpr = EED->getRawValueUnchecked())
Expand Down
9 changes: 8 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4897,8 +4897,15 @@ void PrintAST::printEnumElement(EnumElementDecl *elt) {
break;
}

// Whether a raw value was written explicitly is determined from the original
// (pre-folded) expression: constant folding produces an implicit literal.
// The folded value is what gets printed (e.g. '2 + 3' prints as '5').
if (auto *original = elt->getOriginalRawValueExpr();
!original || original->isImplicit())
return;

auto *raw = elt->getRawValueExpr();
if (!raw || raw->isImplicit())
if (!raw)
return;

// Print the explicit raw value expression.
Expand Down
34 changes: 21 additions & 13 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2954,15 +2954,26 @@ VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const {

bool PatternBindingDecl::hasSingleVarConstantFoldedInit() const {
auto *singleVar = getSingleVar();
return singleVar && singleVar->isConstValue() &&
getASTContext().LangOpts.hasFeature(Feature::LiteralExpressions);
if (!singleVar || !singleVar->isConstValue())
return false;
// Only stdlib integer constants participate in literal-expression folding.
// Other constant initializers (tuples, arrays, strings, ...) are left as
// written and must not be routed through the integer constant-folder.
Type type = singleVar->getInterfaceType();
return type && type->isStdlibInteger();
}

Expr *PatternBindingDecl::getExecutableInit(unsigned i) const {
auto idxInit = getPatternList()[i].getExecutableInit();
if (auto &ctx = getASTContext(); idxInit && hasSingleVarConstantFoldedInit())
return evaluateOrDefault(ctx.evaluator,
ConstantFoldExpression{idxInit, &ctx}, {});
// Code generation: fold silently. Any diagnostics about a non-foldable
// '@const'/'@section' initializer are the verifier's responsibility
// (diagnoseInvalidConstExpressions), not code generation's -- emitting them
// here would duplicate/preempt those and set the error flag that makes the
// const-value SIL passes bail.
return evaluateOrDefault(
ctx.evaluator,
ConstantFoldExpression{idxInit, &ctx, /*emitDiagnostics=*/false}, {});
return idxInit;
}

Expand Down Expand Up @@ -12259,18 +12270,15 @@ LiteralExpr *EnumElementDecl::getRawValueExpr() const {
// 'EnumRawValuesRequest' so this is meant to return the
// cached result, if the above request was successful.
if (RawValueExpr)
if (getASTContext().LangOpts.hasFeature(Feature::LiteralExpressions))
return dyn_cast<LiteralExpr>(evaluateOrDefault(
getASTContext().evaluator,
ConstantFoldExpression{RawValueExpr, &getASTContext()}, {}));
else
return dyn_cast<LiteralExpr>(RawValueExpr);
else
return nullptr;
return dyn_cast<LiteralExpr>(evaluateOrDefault(
getASTContext().evaluator, ConstantFoldExpression{
RawValueExpr, &getASTContext(),
/*emitDiagnostics*/ true}, {}));
return nullptr;
}

void EnumElementDecl::setRawValueExpr(Expr *e) {
assert((!RawValueExpr || e == RawValueExpr || e->getType()) &&
assert((!RawValueExpr || e == RawValueExpr || !e || e->getType()) &&
"Illegal mutation of raw value expr");
RawValueExpr = e;
}
Expand Down
1 change: 0 additions & 1 deletion lib/ASTGen/Sources/ASTGen/SourceFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ extension Parser.ExperimentalFeatures {
mapFeature(.KeyPathWithMethodMembers, to: .keypathWithMethodMembers)
mapFeature(.DefaultIsolationPerFile, to: .defaultIsolationPerFile)
mapFeature(.BorrowAndMutateAccessors, to: .borrowAndMutateAccessors)
mapFeature(.LiteralExpressions, to: .literalExpressions)
}
}

Expand Down
22 changes: 0 additions & 22 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9715,20 +9715,6 @@ ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
return DCC.fixupParserResult(Status, ED);
}

static bool isValidEnumRawValueLiteral(Expr *expr) {
if (expr == nullptr)
return false;

if (!isa<IntegerLiteralExpr>(expr) &&
!isa<FloatLiteralExpr>(expr) &&
!isa<StringLiteralExpr>(expr) &&
!isa<BooleanLiteralExpr>(expr) &&
!isa<NilLiteralExpr>(expr))
return false;

return true;
}

/// Parse a 'case' of an enum.
///
/// \verbatim
Expand Down Expand Up @@ -9843,14 +9829,6 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags,
Status.setIsParseError();
return Status;
}

if (!Context.LangOpts.hasFeature(Feature::LiteralExpressions)) {
if (!isValidEnumRawValueLiteral(RawValueExpr.getPtrOrNull())) {
diagnose(RawValueExpr.getPtrOrNull()->getLoc(),
diag::nonliteral_enum_case_raw_value);
RawValueExpr = nullptr;
}
}
}

// For recovery, again make sure the user didn't try to spell a switch
Expand Down
61 changes: 51 additions & 10 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1612,15 +1612,22 @@ ParserResult<TypeRepr> Parser::parseTypeOrValue(Diag<> MessageID,
// Look ahead to consider if this is a generic value expression
// or possibly a tuple type with a postfix grammar
bool shouldParseValueExpr = false;
if (Context.LangOpts.hasFeature(Feature::LiteralExpressions) &&
Tok.is(tok::l_paren)) {
if (Tok.is(tok::l_paren)) {
BacktrackingScope backtrack(*this);
skipSingle();
if (Tok.is(tok::comma) || startsWithGreater(Tok) ||
(Tok.isContextualKeyword("of") && !Tok.isAtStartOfLine()))
shouldParseValueExpr = true;
}

// A parenthesized expression that contains type-only syntax such as an opaque
// 'some' type, or a top-level comma (making it a tuple type), must be parsed
// as a type, not a value expression: such types have to appear as TypeReprs
// in the enclosing declaration, and a tuple is never a generic value argument.
if (shouldParseValueExpr && Tok.is(tok::l_paren) &&
parenGenericArgumentContainsTypeOnlySyntax())
shouldParseValueExpr = false;

if (shouldParseValueExpr) {
// Ensure that constituent references get parsed as declaration references,
// not type references.
Expand All @@ -1633,9 +1640,33 @@ ParserResult<TypeRepr> Parser::parseTypeOrValue(Diag<> MessageID,
}
return makeParserResult(
new (Context) GenericArgumentExprTypeRepr(expr.get(), &Context));
} else {
return parseType(MessageID, reason);
}
return parseType(MessageID, reason);
}

bool Parser::parenGenericArgumentContainsTypeOnlySyntax() {
assert(Tok.is(tok::l_paren) && "not at a parenthesized generic argument");
BacktrackingScope backtrack(*this);
unsigned depth = 0;
do {
// An opaque 'some' type can only appear as a type, never as part of a
// value expression.
if (Tok.isContextualKeyword("some")) {
BacktrackingScope probe(*this);
if (canParseType())
return true;
}
// A top-level comma makes this a tuple type; tuples are never valid generic
// value arguments, so parse it as a type.
if (depth == 1 && Tok.is(tok::comma))
return true;
if (Tok.isAny(tok::l_paren, tok::l_square, tok::l_brace))
++depth;
else if (Tok.isAny(tok::r_paren, tok::r_square, tok::r_brace))
--depth;
consumeToken();
} while (depth > 0 && Tok.isNot(tok::eof));
return false;
}

bool Parser::canParseGenericValueLiteral() {
Expand Down Expand Up @@ -1732,11 +1763,22 @@ bool Parser::canParseGenericArguments() {
return true;
}

do {
if (Context.LangOpts.hasFeature(Feature::LiteralExpressions) &&
Tok.is(tok::l_paren))
do {
// A generic argument may be a parenthesized value expression such as
// '(1 + 2)'. Treat a parenthesized group as a value expression only when
// it is immediately followed by ',' or '>'; otherwise parse it as a type
// so that parenthesized and function types like '(Int, Int) -> Bool' are
// still recognized.
bool parsedValueExpr = false;
if (Tok.is(tok::l_paren)) {
CancellableBacktrackingScope backtrack(*this);
skipSingle();
else if (!canParseType())
if (Tok.is(tok::comma) || startsWithGreater(Tok)) {
backtrack.cancelBacktrack();
parsedValueExpr = true;
}
}
if (!parsedValueExpr && !canParseType())
return false;

// Parse the comma, if the list continues.
Expand Down Expand Up @@ -1959,8 +2001,7 @@ bool Parser::canParseStartOfInlineArrayType() {
// expression or type. We specifically look for any type, not just integers
// for better recovery in e.g cases where the user writes '[Int of 2]'. We
// only do type-scalar since variadics would be ambiguous e.g 'Int...of'.
if (Context.LangOpts.hasFeature(Feature::LiteralExpressions) &&
Tok.is(tok::l_paren))
if (Tok.is(tok::l_paren))
skipSingle(); // Assume a parentheses-delimited value expression
else if (!canParseTypeScalar())
return false;
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/LegalLiteralExprVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,7 @@ void swift::diagnoseInvalidConstExpressions(const Expr *expr,
if (auto error = checkSupportedInConst(expr, declContext))
diagnoseError(error->first->getLoc(), error->second, ctx.Diags);
} else {
if (ctx.LangOpts.hasFeature(Feature::LiteralExpressions) &&
expr->getType()->isStdlibInteger())
if (expr->getType()->isStdlibInteger())
foldLiteralExpression(expr, &ctx);
else if (auto error =
checkSupportedWithSectionAttribute(expr, declContext))
Expand Down
Loading