How do the different enum variants work in TypeScript? - enums

TypeScript has a bunch of different ways to define an enum:
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
If I try to use a value from Gamma at runtime, I get an error because Gamma is not defined, but that's not the case for Delta or Alpha? What does const or declare mean on the declarations here?
There's also a preserveConstEnums compiler flag -- how does this interact with these?

There are four different aspects to enums in TypeScript you need to be aware of. First, some definitions:
"lookup object"
If you write this enum:
enum Foo { X, Y }
TypeScript will emit the following object:
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
I'll refer to this as the lookup object. Its purpose is twofold: to serve as a mapping from strings to numbers, e.g. when writing Foo.X or Foo['X'], and to serve as a mapping from numbers to strings. That reverse mapping is useful for debugging or logging purposes -- you will often have the value 0 or 1 and want to get the corresponding string "X" or "Y".
"declare" or "ambient"
In TypeScript, you can "declare" things that the compiler should know about, but not actually emit code for. This is useful when you have libraries like jQuery that define some object (e.g. $) that you want type information about, but don't need any code created by the compiler. The spec and other documentation refers to declarations made this way as being in an "ambient" context; it is important to note that all declarations in a .d.ts file are "ambient" (either requiring an explicit declare modifier or having it implicitly, depending on the declaration type).
"inlining"
For performance and code size reasons, it's often preferable to have a reference to an enum member replaced by its numeric equivalent when compiled:
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
The spec calls this substitution, I will call it inlining because it sounds cooler. Sometimes you will not want enum members to be inlined, for example because the enum value might change in a future version of the API.
Enums, how do they work?
Let's break this down by each aspect of an enum. Unfortunately, each of these four sections is going to reference terms from all of the others, so you'll probably need to read this whole thing more than once.
computed vs non-computed (constant)
Enum members can either be computed or not. The spec calls non-computed members constant, but I'll call them non-computed to avoid confusion with const.
A computed enum member is one whose value is not known at compile-time. References to computed members cannot be inlined, of course. Conversely, a non-computed enum member is once whose value is known at compile-time. References to non-computed members are always inlined.
Which enum members are computed and which are non-computed? First, all members of a const enum are constant (i.e. non-computed), as the name implies. For a non-const enum, it depends on whether you're looking at an ambient (declare) enum or a non-ambient enum.
A member of a declare enum (i.e. ambient enum) is constant if and only if it has an initializer. Otherwise, it is computed. Note that in a declare enum, only numeric initializers are allowed. Example:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
Finally, members of non-declare non-const enums are always considered to be computed. However, their initializing expressions are reduced down to constants if they're computable at compile-time. This means non-const enum members are never inlined (this behavior changed in TypeScript 1.5, see "Changes in TypeScript" at the bottom)
const vs non-const
const
An enum declaration can have the const modifier. If an enum is const, all references to its members inlined.
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const enums do not produce a lookup object when compiled. For this reason, it is an error to reference Foo in the above code except as part of a member reference. No Foo object will be present at runtime.
non-const
If an enum declaration does not have the const modifier, references to its members are inlined only if the member is non-computed. A non-const, non-declare enum will produce a lookup object.
declare (ambient) vs non-declare
An important preface is that declare in TypeScript has a very specific meaning: This object exists somewhere else. It's for describing existing objects. Using declare to define objects that don't actually exist can have bad consequences; we'll explore those later.
declare
A declare enum will not emit a lookup object. References to its members are inlined if those members are computed (see above on computed vs non-computed).
It's important to note that other forms of reference to a declare enum are allowed, e.g. this code is not a compile error but will fail at runtime:
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
This error falls under the category of "Don't lie to the compiler". If you don't have an object named Foo at runtime, don't write declare enum Foo!
A declare const enum is not different from a const enum, except in the case of --preserveConstEnums (see below).
non-declare
A non-declare enum produces a lookup object if it is not const. Inlining is described above.
--preserveConstEnums flag
This flag has exactly one effect: non-declare const enums will emit a lookup object. Inlining is not affected. This is useful for debugging.
Common Errors
The most common mistake is to use a declare enum when a regular enum or const enum would be more appropriate. A common form is this:
module MyModule {
// Claiming this enum exists with 'declare', but it doesn't...
export declare enum Lies {
Foo = 0,
Bar = 1
}
var x = Lies.Foo; // Depend on inlining
}
module SomeOtherCode {
// x ends up as 'undefined' at runtime
import x = MyModule.Lies;
// Try to use lookup object, which ought to exist
// runtime error, canot read property 0 of undefined
console.log(x[x.Foo]);
}
Remember the golden rule: Never declare things that don't actually exist. Use const enum if you always want inlining, or enum if you want the lookup object.
Changes in TypeScript
Between TypeScript 1.4 and 1.5, there was a change in the behavior (see https://github.com/Microsoft/TypeScript/issues/2183) to make all members of non-declare non-const enums be treated as computed, even if they're explicitly initialized with a literal. This "unsplit the baby", so to speak, making the inlining behavior more predictable and more cleanly separating the concept of const enum from regular enum. Prior to this change, non-computed members of non-const enums were inlined more aggressively.

There are a few things going on here. Let's go case by case.
enum
enum Cheese { Brie, Cheddar }
First, a plain old enum. When compiled to JavaScript, this will emit a lookup table.
The lookup table looks like this:
var Cheese;
(function (Cheese) {
Cheese[Cheese["Brie"] = 0] = "Brie";
Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));
Then when you have Cheese.Brie in TypeScript, it emits Cheese.Brie in JavaScript which evaluates to 0. Cheese[0] emits Cheese[0] and actually evaluates to "Brie".
const enum
const enum Bread { Rye, Wheat }
No code is actually emitted for this! Its values are inlined. The following emit the value 0 itself in JavaScript:
Bread.Rye
Bread['Rye']
const enums' inlining might be useful for performance reasons.
But what about Bread[0]? This will error out at runtime and your compiler should catch it. There's no lookup table and the compiler doesn't inline here.
Note that in the above case, the --preserveConstEnums flag will cause Bread to emit a lookup table. Its values will still be inlined though.
declare enum
As with other uses of declare, declare emits no code and expects you to have defined the actual code elsewhere. This emits no lookup table:
declare enum Wine { Red, Wine }
Wine.Red emits Wine.Red in JavaScript, but there won't be any Wine lookup table to reference so it's an error unless you've defined it elsewhere.
declare const enum
This emits no lookup table:
declare const enum Fruit { Apple, Pear }
But it does inline! Fruit.Apple emits 0. But again Fruit[0] will error out at runtime because it's not inlined and there's no lookup table.
I've written this up in this playground. I recommend playing there to understand which TypeScript emits which JavaScript.

Related

Why does delegating to the default constructor not zero initialize member variable

#include <string>
struct T1 {
int _mem1;
int _mem2;
T1() = default;
T1(int mem2) : T1() { _mem2 = mem2; }
};
T1 getT1() { return T1(); }
T1 getT1(int mem2) { return T1(mem2); }
int main() {
volatile T1 a = T1();
std::printf("a._mem1=%d a._mem2=%d\n", a._mem1, a._mem2);
volatile T1 b = T1(1);
std::printf("b._mem1=%d b._mem2=%d\n", b._mem1, b._mem2);
// Temporarily disable
if (false) {
volatile T1 c = getT1();
std::printf("c._mem1=%d c._mem2=%d\n", c._mem1, c._mem2);
volatile T1 d = getT1(1);
std::printf("d._mem1=%d d._mem2=%d\n", d._mem1, d._mem2);
}
}
When I compile this with gcc5.4, I get the following output:
g++ -std=c++11 -O3 test.cpp -o test && ./test
a._mem1=0 a._mem2=0
b._mem1=382685824 b._mem2=1
Why does the user defined constructor, which delegates to the default constructor not manage to set _mem1 to zero for b, however a which uses the default constructor is zero initialized?
Valgrind confirms this also:
==12579== Conditional jump or move depends on uninitialised value(s)
==12579== at 0x4E87CE2: vfprintf (vfprintf.c:1631)
==12579== by 0x4E8F898: printf (printf.c:33)
==12579== by 0x4005F3: main (in test)
If I change if(false) to if(true)
Then the output is as you would expect
a._mem1=0 a._mem2=0
b._mem1=0 b._mem2=1
c._mem1=0 c._mem2=0
d._mem1=0 d._mem2=1
What is the compiler doing?
Short answer: for trivial types, the two distinct forms of "default construction" leads to two different initializations:
T a; in which case the object is default-initialized. Its value is undetermined and undefined behavior will soon happen (this is how is initialized b.mem1 and why valgrind detect an error.)
T a=T(); in which case the object is value-initialized and its entire memory is zeroed (this is what happens to a.mem1 and a.mem2)
Long answer: Actualy, the default constructor of T1 is not the cause of zero initialization of a.mem1. a has been first zero-initialized but not b because of a singular rule of the standard that does not apply for b's initializer.
The definition volatile a=T() causes a to be value-initialized (1). struct T1 as no user-provided default constructor (2). For such a struct the entire object is zero-initialized as stated by this rule of the C++11 standard [dcl.init]/7.2:
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is called.
There is a subtle difference between C++11 and C++17 that causes the definition volatile b=T(1) to be undefined behavior in C++11 but not in C++17. In C++11, b is initialized by copying an object type T1 which is initialized by the expression T(1). This copy construction evaluate T(1).mem1 which is an undetermined value. This is forbidden. In c++17, b is directly initialized by the prvalue expression T(1).
The evaluation of this undetermined value inside the printf is also undefined behavior independently of the c++ standard. This is why valgrind complains and why you see inconsistent outputs when you change if (true) to if (false).
(1) strictly speaking a is copy constructed from a value-initalized object in c++11
(2) T1's default constructor is not user provided because it is defined as defaulted on the first declaration
Short Answer
The default constructor in your code is considered trivial and that kind of constructor perform no actions i.e. leave things unitialized.
Longer answer
Trivial default constructor
The default constructor for class T is trivial (i.e. performs no
action) if all of the following is true:
The constructor is not user-provided (i.e., is implicitly-defined or defaulted on its first declaration)
T has no virtual member functions
T has no virtual base classes
T has no non-static members with default initializers.
(since C++11)
Every direct base of T has a trivial default constructor
Every non-static member of class type has a trivial default constructor
> A trivial default constructor is a constructor that performs no
action. All data types compatible with the C language (POD types) are
trivially default-constructible. Unlike in C, however, objects with
trivial default constructors cannot be created by simply
reinterpreting suitably aligned storage, such as memory allocated with
std::malloc: placement-new is required to formally introduce a new
object and avoid potential undefined behavior.
http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/cpp/language/default_constructor.html

Why doesn't Typescript Intellisense show Object extensions?

Consider this code to extend the Object type:
interface Object
{
doSomething() : void;
}
Object.prototype.doSomething = function ()
{
//do something
}
With this in place, the following both compile:
(this as Object).doSomething();
this.doSomething();
BUT: when I'm typing the first line, Intellisense knows about the doSomething method and shows it in the auto-completion list. When I'm typing the second line, it does not.
I'm puzzled about this, because doesn't every variable derive from Object, and therefore why doesn't Visual Studio show the extra method in the method list?
Update:
Even though the Intellisense doesn't offer the method, it does seem to recognize it when I've typed it manually:
What could explain that?!
...because doesn't every variable derive from Object
No, for two reasons:
1. JavaScript (and TypeScript) has both objects and primitives. this can hold any value (in strict mode), and consequently can be a primitive:
"use strict";
foo();
foo.call(42);
function foo() {
console.log(typeof this);
}
Here's that same code in the TypeScript playground. In both cases (here and there), the above outputs:
undefined
number
...neither of which is derived from Object.
2. Not all objects inherit from Object.prototype:
var obj = Object.create(null);
console.log(typeof obj.toString); // undefined
console.log("toString" in obj); // false
If an object's prototype chain is rooted in an object that doesn't have a prototype at all (like obj above), it won't have the features of Object.prototype.
From your comment below:
I thought even primitives like number inherit from Object. If number doesn't, how does number.ToString() work?
Primitives are primitives, which don't inherit from Object. But you're right that most of them seem to, because number, string, boolean, and symbol have object counterparts (Number, String, Boolean, and Symbol) which do derive from Object. But not all primitives do: undefined and null throw a TypeError if you try to treat them like objects. (Yes, null is a primitive even though typeof null is "object".)
For the four of them that have object counterparts, when you use a primitive like an object like this:
var a = 42;
console.log(a.toString());
...an appropriate type of object is created and initialized from the primitive via the abstract ToObject operation in the spec, and the resulting object's method is called; then unless that method returns that object reference (I don't think any built-in method does, but you can add one that does), the temporary object is immediately eligible for garbage collection. (Naturally, JavaScript engines optimize this process in common cases like toString and valueOf.)
You can tell the object is temporary by doing something like this:
var a = 42;
console.log(a); // 42
console.log(typeof a); // "number"
a.foo = "bar"; // temp object created and released
console.log(a.foo); // undefined, the object wasn't assigned back to `a`
var b = new Number(42);
console.log(b); // (See below)
console.log(typeof b); // "object"
b.foo = "bar"; // since `b` refers to an object, the property...
console.log(b.foo); // ... is retained: "bar"
(Re "see below": In the Stack Snippets console, you see {} there; in Chrome's real console, what you see depends on whether you have the console open: If you don't, opening it later will show you 42; if you do, you'll see ▶ Number {[[PrimitiveValue]]: 42} which you can expand with the ▶.)
Does number implement its own toString method, having nothing to do with Object?
Yes, but that doesn't really matter re your point about primitives and their odd relationship with Object.
So to round up:
this may contain a primitive, and while some primitives can be treated like objects, not all can.
this may contain an object reference for an object that doesn't derive from Object (which is to say, doesn't have Object.prototype in its prototype chain).
JavaScript is a hard language for IntelliSense. :-)

Lambda expression in c++, OS X's clang vs GCC

A particular property of c++'s lambda expressions is to capture the variables in the scope in which they are declared. For example I can use a declared and initialized variable c in a lambda function even if 'c' is not sent as an argument, but it's captured by '[ ]':
#include<iostream>
int main ()
{int c=5; [c](int d){std::cout<<c+d<<'\n';}(5);}
The expected output is thus 10. The problem arises when at least 2 variables, one captured and the other sent as an argument, have the same name:
#include<iostream>
int main ()
{int c=5; [c](int c){std::cout<<c<<'\n';}(3);}
I think that the 2011 standard for c++ says that the captured variable has the precedence on the arguments of the lambda expression in case of coincidence of names. In fact compiling the code using GCC 4.8.1 on Linux the output I get is the expected one, 5. If I compile the same code using apple's version of clang compiler (clang-503.0.40, the one which comes with Xcode 5.1.1 on Mac OS X 10.9.4) I get the other answer, 3.
I'm trying to figure why this happens; is it just an apple's compiler bug (if the standard for the language really says that the captured 'c' has the precedence) or something similar? Can this issue be fixed?
EDIT
My teacher sent an email to GCC help desk, and they answered that it's clearly a bug of GCC compiler and to report it to Bugzilla. So Clang's behavior is the correct one!
From my understanding of the c++11 standard's points below:
5.1.2 Lambda expressions
3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called
the closure type — whose properties are described below.
...
5 The closure type for a lambda-expression has a public inline function call operator (13.5.4) whose parameters and return type are
described by the lambda-expression’s parameter-declaration-clause and
trailing-return-type respectively. This function call operator is
declared const (9.3.1) if and only if the lambda-expression’s
parameter-declaration-clause is not followed by mutable.
...
14 For each entity captured by copy, an unnamed non static data member is declared in the closure type
A lambda expression like this...
int c = 5;
[c](int c){ std::cout << c << '\n'; }
...is roughly equivalent to a class/struct like this:
struct lambda
{
int c; // captured c
void operator()(int c) const
{
std::cout << c << '\n';
}
};
So I would expect the parameter to hide the captured member.
EDIT:
In point 14 from the standard (quoted above) it would seem the data member created from the captured variable is * unnamed *. The mechanism by which is it referenced appears to be independent of the normal identifier lookups:
17 Every id-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.
It is unclear from my reading of the standard if this transformation should take precedence over parameter symbol lookup.
So perhaps this should be marked as UB (undefined behaviour)?
From the C++11 Standard, 5.1.2 "Lambda expressions" [expr.prim.lambda] #7:
The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator,
but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming id-expressions
referring to non-static class members into class member access expressions using (*this) (9.3.1),
the compound-statement is considered in the context of the lambda-expression.
Also, from 3.3.3 "Block scope" [basic.scope.local] #2:
The potential scope of a function parameter name (including one appearing in a lambda-declarator) or of
a function-local predefined variable in a function definition (8.4) begins at its point of declaration.
Names in a capture list are not declarations and therefore do not affect name lookup. The capture list just allows you to use the local variables; it does not introduce their names into the lambda's scope. Example:
int i, j;
int main()
{
int i = 0;
[](){ i; }; // Error: Odr-uses non-static local variable without capturing it
[](){ j; }; // OK
}
So, since the parameters to a lambda are in an inner block scope, and since name lookup is done in the context of the lambda expression (not, say, the generated class), the parameter names indeed hide the variable names in the enclosing function.

Whats the point of a move-constructor taking a constans rvalue?

According to http://en.cppreference.com/w/cpp/language/move_constructor; "A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&)"
When would one want to pass a constant rvalue to the move-constructor?
The short answer is "never".
However, you can indeed write a function like this:
Foo const f();
Now something like Foo x = f(); would use the Foo const && constructor if it is available. But it will also bind to the Foo const & constructor with pleasure, and since both references are constant, there's really no need for the rvalue version.
In particular, there's mothing "move" about that constructor, since, as we established, the reference is constant.

Why would const-ness of a local variable inhibit move semantics for the returned value?

struct STest : public boost::noncopyable {
STest(STest && test) : m_n( std::move(test.m_n) ) {}
explicit STest(int n) : m_n(n) {}
int m_n;
};
STest FuncUsingConst(int n) {
STest const a(n);
return a;
}
STest FuncWithoutConst(int n) {
STest a(n);
return a;
}
void Caller() {
// 1. compiles just fine and uses move ctor
STest s1( FuncWithoutConst(17) );
// 2. does not compile (cannot use move ctor, tries to use copy ctor)
STest s2( FuncUsingConst(17) );
}
The above example illustrates how in C++11, as implemented in Microsoft Visual C++ 2012, the internal details of a function can modify its return type. Up until today, it was my understanding that the declaration of the return type is all a programmer needs to know to understand how the return value will be treated, e.g., when passed as a parameter to a subsequent function call. Not so.
I like making local variables const where appropriate. It helps me clean up my train of thought and clearly structure an algorithm. But beware of returning a variable that was declared const! Even though the variable will no longer be accessed (a return statement was executed, after all), and even though the variable that was declared const has long gone out of scope (evaluation of the parameter expression is complete), it cannot be moved and thus will be copied (or fail to compile if copying is not possible).
This question is related to another question, Move semantics & returning const values. The difference is that in the latter, the function is declared to return a const value. In my example, FuncUsingConst is declared to return a volatile temporary. Yet, the implementational details of the function body affect the type of the return value, and determine whether or not the returned value can be used as a parameter to other functions.
Is this behavior intended by the standard?
How can this be regarded useful?
Bonus question: How can the compiler know the difference at compile time, given that the call and the implementation may be in different translation units?
EDIT: An attempt to rephrase the question.
How is it possible that there is more to the result of a function than the declared return type? How does it even seem acceptable at all that the function declaration is not sufficient to determine the behavior of the function's returned value? To me that seems to be a case of FUBAR and I'm just not sure whether to blame the standard or Microsoft's implementation thereof.
As the implementer of the called function, I cannot be expected to even know all callers, let alone monitor every little change in the calling code. On the other hand, as the implementer of the calling function, I cannot rely on the called function to not return a variable that happens to be declared const within the scope of the function implementation.
A function declaration is a contract. What is it worth now? We are not talking about a semantically equivalent compiler optimization here, like copy elision, which is nice to have but does not change the meaning of code. Whether or not the copy ctor is called does change the meaning of code (and can even break the code to a degree that it cannot be compiled, as illustrated above). To appreciate the awkwardness of what I am discussing here, consider the "bonus question" above.
I like making local variables const where appropriate. It helps me clean up my train of thought and clearly structure an algorithm.
That is indeed a good practice. Use const wherever you can. Here, however, you cannot (if you expect your const object to be moved from).
The fact that you declare a const object inside your function is a promise that your object's state won't ever be altered as long as the object is alive - in other words, never before its destructor is invoked. Not even immediately before its destructor is invoked. As long as it is alive, the state of a const object shall not change.
However, here you are somehow expecting this object to be moved from right before it gets destroyed by falling out of scope, and moving is altering state. You cannot move from a const object - not even if you are not going to use that object anymore.
What you can do, however, is to create a non-const object and access it in your function only through a reference to const bound to that object:
STest FuncUsingConst(int n) {
STest object_not_to_be_touched_if_not_through_reference(n);
STest const& a = object_not_to_be_touched_if_not_through_reference;
// Now work only with a
return object_not_to_be_touched_if_not_through_reference;
}
With a bit of discipline, you can easily enforce the semantics that the function should not modify that object after its creation - except for being allowed to move from it when returning.
UPDATE:
As suggested by balki in the comments, another possibility would be to bind a constant reference to a non-const temporary object (whose lifetime would be prolonged as per § 12.2/5), and perform a const_cast when returning it:
STest FuncUsingConst(int n) {
STest const& a = STest();
// Now work only with a
return const_cast<STest&&>(std::move(a));
}
A program is ill-formed if the copy/move constructor [...] for an object is implicitly odr-used and the special member function is not accessible
-- n3485 C++ draft standard [class.copy]/30
I suspect your problem is with MSVC 2012, and not with C++11.
This code, even without calling it, is not legal C++11:
struct STest {
STest(STest const&) = delete
STest(STest && test) : m_n( std::move(test.m_n) ) {}
explicit STest(int n) : m_n(n) {}
int m_n;
};
STest FuncUsingConst(int n) {
STest const a(n);
return a;
}
because there is no legal way to turn a into a return value. While the return can be elided, eliding the return value does not remove the requirement that the copy constructor exist.
If MSVC2012 is allowing FuncUsingConst to compile, it is doing so in violation of the C++11 standard.

Resources