diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index f5482fcddec..f311a649d4b 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -491,6 +491,7 @@ public: return getSemanticsProvidingExpr()->getKind() == ExprKind::InOut; } + bool printConstExprValue(llvm::raw_ostream *OS) const; bool isSemanticallyConstExpr() const; /// Returns false if this expression needs to be wrapped in parens when diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index d955a429f4e..275b77051c5 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -2220,8 +2220,12 @@ static parseJsonEmit(SDKContext &Ctx, StringRef FileName) { return {std::move(FileBufOrErr.get()), Result}; } enum class ConstKind: uint8_t { - String = 0, - Int, + StringLiteral = 0, + IntegerLiteral, + FloatLiteral, + BooleanLiteral, + Array, + Dictionary, }; struct ConstExprInfo { @@ -2230,9 +2234,11 @@ struct ConstExprInfo { unsigned offset = 0; unsigned length = 0; StringRef value; + StringRef referencedD; ConstExprInfo(StringRef filePath, ConstKind kind, unsigned offset, - unsigned length, StringRef value): - filePath(filePath), kind(kind), offset(offset), length(length), value(value) {} + unsigned length, StringRef value, StringRef referencedD): + filePath(filePath), kind(kind), offset(offset), length(length), value(value), + referencedD(referencedD) {} ConstExprInfo() = default; }; @@ -2242,7 +2248,7 @@ class ConstExtractor: public ASTWalker { SourceManager &SM; std::vector allConsts; - void record(Expr *E, ConstKind kind, StringRef Value) { + void record(Expr *E, ConstKind kind, StringRef Value, StringRef ReferencedD) { auto startLoc = E->getStartLoc(); // Asserts? if (startLoc.isInvalid()) @@ -2254,14 +2260,70 @@ class ConstExtractor: public ASTWalker { auto length = SM.getByteDistance(startLoc, endLoc); auto file = SM.getIdentifierForBuffer(bufferId); auto offset = SM.getLocOffsetInBuffer(startLoc, bufferId); - allConsts.emplace_back(file, kind, offset, length, Value); + allConsts.emplace_back(file, kind, offset, length, Value, ReferencedD); } + void record(Expr *E, Expr *ValueProvider, StringRef ReferecedD = "") { + std::string content; + llvm::raw_string_ostream os(content); + ValueProvider->printConstExprValue(&os); + assert(!content.empty()); + auto buffered = SCtx.buffer(content); + switch(ValueProvider->getKind()) { +#define CASE(X) case ExprKind::X: record(E, ConstKind::X, buffered, ReferecedD); break; + CASE(StringLiteral) + CASE(IntegerLiteral) + CASE(FloatLiteral) + CASE(BooleanLiteral) + CASE(Dictionary) + CASE(Array) +#undef CASE + default: + return; + } + } + + StringRef getDeclName(Decl *D) { + if (auto *VD = dyn_cast(D)) { + std::string content; + llvm::raw_string_ostream os(content); + VD->getName().print(os); + return SCtx.buffer(content); + } + return StringRef(); + } + + bool handleSimpleReference(Expr *E) { + assert(E); + Decl *ReferencedDecl = nullptr; + if (auto *MRE = dyn_cast(E)) { + ReferencedDecl = MRE->getDecl().getDecl(); + } else if (auto *DRE = dyn_cast(E)) { + ReferencedDecl = DRE->getDecl(); + } else { + return false; + } + assert(ReferencedDecl); + if (auto *VAR = dyn_cast(ReferencedDecl)) { + if (!VAR->getAttrs().hasAttribute()) { + return false; + } + if (auto *PD = VAR->getParentPatternBinding()) { + if (auto *init = PD->getInit(PD->getPatternEntryIndexForVarDecl(VAR))) { + record(E, init, getDeclName(ReferencedDecl)); + return true; + } + } + } + return false; + } std::pair walkToExprPre(Expr *E) override { if (E->isSemanticallyConstExpr()) { - if (auto *SL = dyn_cast(E)) { - record(SL, ConstKind::String, SL->getValue()); - } + record(E, E); + return { false, E }; + } + if (handleSimpleReference(E)) { + return { false, E }; } return { true, E }; } @@ -2279,14 +2341,20 @@ template <> struct swift::json::ObjectTraits { StringRef kind; switch(info.kind) { #define CASE(X) case ConstKind::X: kind = #X; break; - CASE(String) - CASE(Int) + CASE(StringLiteral) + CASE(IntegerLiteral) + CASE(FloatLiteral) + CASE(BooleanLiteral) + CASE(Dictionary) + CASE(Array) #undef CASE } out.mapRequired("kind", kind); out.mapRequired("offset", info.offset); out.mapRequired("length", info.length); out.mapRequired("value", info.value); + if (!info.referencedD.empty()) + out.mapRequired("decl", info.referencedD); } }; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 966e72fba28..385889709f5 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -198,35 +198,72 @@ Expr *Expr::getSemanticsProvidingExpr() { return this; } -bool Expr::isSemanticallyConstExpr() const { - auto E = getSemanticsProvidingExpr(); - if (!E) { - return false; - } - switch(E->getKind()) { - case ExprKind::IntegerLiteral: - case ExprKind::NilLiteral: - case ExprKind::BooleanLiteral: - case ExprKind::FloatLiteral: - case ExprKind::StringLiteral: - case ExprKind::KeyPath: +bool Expr::printConstExprValue(llvm::raw_ostream *OS) const { + auto print = [&](StringRef text) { + if (OS) { + *OS << text; + } + }; + auto *E = getSemanticsProvidingExpr(); + assert(E); + switch(getKind()) { + case ExprKind::BooleanLiteral: { + auto isTrue = cast(E)->getValue(); + print(isTrue ? "true" : "false"); return true; + } + case ExprKind::IntegerLiteral: + case ExprKind::FloatLiteral: { + auto digits = cast(E)->getDigitsText(); + assert(!digits.empty()); + print(digits); + return true; + } + case ExprKind::NilLiteral: { + print("nil"); + return true; + } + case ExprKind::StringLiteral: { + auto *LE = cast(E); + print("\""); + print(LE->getValue()); + print("\""); + return true; + } + case ExprKind::KeyPath: { + // FIXME: print keypath + print("\\."); + return true; + } case ExprKind::Array: case ExprKind::Dictionary: { + print("["); auto *CE = cast(E); - for (auto *EL: CE->getElements()) { - if (!EL->isSemanticallyConstExpr()) + for (unsigned N = CE->getNumElements(), I = 0; I != N; I ++) { + auto Ele = CE->getElement(I); + auto needComma = I + 1 != N; + if (!Ele->printConstExprValue(OS)) { return false; + } + if (needComma) + print(", "); } + print("]"); return true; } case ExprKind::Tuple: { + print("("); auto *TE = cast(E); - for (auto *EL: TE->getElements()) { - if (!EL->isSemanticallyConstExpr()) { + for (unsigned N = TE->getNumElements(), I = 0; I != N; I ++) { + auto Ele = TE->getElement(I); + auto needComma = I + 1 != N; + if (!Ele->printConstExprValue(OS)) { return false; } + if (needComma) + print(", "); } + print(")"); return true; } default: @@ -234,6 +271,10 @@ bool Expr::isSemanticallyConstExpr() const { } } +bool Expr::isSemanticallyConstExpr() const { + return printConstExprValue(nullptr); +} + Expr *Expr::getValueProvidingExpr() { Expr *E = getSemanticsProvidingExpr(); diff --git a/test/api-digester/Inputs/ConstExtraction/SimpleReferences.swift b/test/api-digester/Inputs/ConstExtraction/SimpleReferences.swift new file mode 100644 index 00000000000..23b19242fb0 --- /dev/null +++ b/test/api-digester/Inputs/ConstExtraction/SimpleReferences.swift @@ -0,0 +1,18 @@ +_const let global_str = "abc" +_const let global_int = 3 +_const let global_float = 3.2 + +class C { + static _const let class_bool = false + static _const let class_arr = [2, 2, 3] + static _const let class_dict = [2:1, 2:1, 3:1] +} + +func foo() { + _ = global_str + _ = global_int + _ = global_float + _ = C.class_bool + _ = C.class_arr + _ = C.class_dict +} diff --git a/test/api-digester/const_values.swift b/test/api-digester/const_values.swift index 2b0e80b9589..16b6c86455c 100644 --- a/test/api-digester/const_values.swift +++ b/test/api-digester/const_values.swift @@ -7,7 +7,7 @@ // RUN: %FileCheck %s < %t/abi.json -// CHECK: "kind": "String" -// CHECK: "value": "abc" -// CHECK: "kind": "String" -// CHECK: "value": "def" +// CHECK: "kind": "StringLiteral" +// CHECK: "value": "\"abc\"" +// CHECK: "kind": "StringLiteral" +// CHECK: "value": "\"def\"" diff --git a/test/api-digester/const_values_simple_references.swift b/test/api-digester/const_values_simple_references.swift new file mode 100644 index 00000000000..564be8d8c7b --- /dev/null +++ b/test/api-digester/const_values_simple_references.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -emit-module -o %t/Foo.swiftmodule -emit-abi-descriptor-path %t/abi.json %S/Inputs/ConstExtraction/SimpleReferences.swift +// RUN: %api-digester -deserialize-sdk -input-paths %t/abi.json -o %t.result + +// RUN: %FileCheck %s < %t/abi.json + +// CHECK: "offset": 249, +// CHECK-NEXT: "length": 10, +// CHECK-NEXT: "value": "\"abc\"", +// CHECK-NEXT: "decl": "global_str" + +// CHECK: "kind": "IntegerLiteral", +// CHECK-NEXT: "offset": 266, +// CHECK-NEXT: "length": 10, +// CHECK-NEXT: "value": "3", +// CHECK-NEXT: "decl": "global_int" + +// CHECK: "kind": "FloatLiteral", +// CHECK-NEXT: "offset": 283, +// CHECK-NEXT: "length": 12, +// CHECK-NEXT: "value": "3.2", +// CHECK-NEXT: "decl": "global_float" + +// CHECK: "kind": "BooleanLiteral", +// CHECK-NEXT: "offset": 302, +// CHECK-NEXT: "length": 12, +// CHECK-NEXT: "value": "false", +// CHECK-NEXT: "decl": "class_bool" + +// CHECK: "kind": "Array", +// CHECK-NEXT: "offset": 321, +// CHECK-NEXT: "length": 11, +// CHECK-NEXT: "value": "[2, 2, 3]", +// CHECK-NEXT: "decl": "class_arr" + +// CHECK: "kind": "Dictionary", +// CHECK-NEXT: "offset": 339, +// CHECK-NEXT: "length": 12, +// CHECK-NEXT: "value": "[(2, 1), (2, 1), (3, 1)]", +// CHECK-NEXT: "decl": "class_dict"